-
題意:每個點都屬於某個公司,公司對應一個權值,對於一條路徑,如果一些點屬於同一家公司,那么貢獻只能算一次,給你一張圖,路徑只能從小的往大的走,現在問你從\(1\)到每個點的路徑上的最大權值是多少。
-
題解:\(n\)最大為\(36\),出現多個點的公司數最大為\(\frac{n}{2}\),不難發現,對於一條路徑,如果這條路徑上的一些點僅有一家公司屬於他們,這種情況是固定的,出現狀態分裂的情況為出現多個點的那些公司,而這題的\(n\)很小,我們可以用二進制來壓縮出現多個點的公司的狀態。
設\(dp[i][j]\)表示,當前在\(i\)點,出現多個點的公司的選擇情況為\(j\),\(to\)表示上一個點,那么有:
1.\(i\)只有一家公司擁有他,那么我們直接從上一個點轉移即可,\(dp[i][j]=max(dp[to][j]+w[c[i]])\).
2.\(i\)屬於出現多個點的公司,那么先找到他在出現多個點的公司的新編號,那么根據當前的\(j\)直接轉移就好
具體看代碼吧,這里注意,有的人可能會說,假如我找到\(i\)的時候,\(j\)的狀態表示了后面一些還沒跑到的點會不會有問題,這里是沒問題的,因為只有跑過的點會對狀態有影響,沒有遍歷到的點選還是不選都一個樣,因為值都是一樣的
-
代碼:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 2e6 + 10; const int mod = 998244353; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int n,m; int c[N],w[N]; int dp[40][N]; vector<int> edge[N]; unordered_map<int,int> mp; vector<int> v; int main() { scanf("%d %d",&n,&m); for(int i=1;i<=n;++i){ scanf("%d",&c[i]); mp[c[i]]++; } for(int i=1;i<=n;++i) scanf("%d",&w[i]); for(auto w:mp){ if(w.se>1) v.pb(w.fi); } for(int i=1;i<=m;++i){ int u,v; scanf("%d %d",&u,&v); edge[v].pb(u); } edge[1].pb(0); for(int i=1;i<=n;++i){ int id=-1; for(int j=0;j<(int)v.size();++j){ if(c[i]==v[j]){ id=j; break; } } if(id==-1){ for(int j=0;j<(1<<(int)v.size());++j){ for(auto to:edge[i]){ dp[i][j]=max(dp[i][j],dp[to][j]+w[c[i]]); } } } else{ for(int j=0;j<(1<<(int)v.size());++j){ if(j&(1<<id)){ for(auto to:edge[i]){ dp[i][j]=max(dp[i][j],dp[to][j^(1<<id)]+w[c[i]]); } } else{ for(auto to:edge[i]){ dp[i][j]=max(dp[i][j],dp[to][j]); } } } } int ans=0; for(int j=0;j<(1<<(int)v.size());++j) ans=max(ans,dp[i][j]);