可以先看一下多校的题解感受一下,,,
题意:
设dp[s]=f(s)-Ws;
则很容易设置dp方程dp[i]=max( dp[j] + Wi pot Wj )( j<i ) pot代表一种操作
显然硬来时间复杂度很高,因为是树形结构,dfs遍历的过程中有很多东西可以记录下来,所以想办法事先把dp[j]通通记录下来
因为数据范围是216 我们设置一个数组ds[x][b],表示当祖先结点的权值的前八位为x,当前结点权值后八位为b时 ds[x][b]= max{dp[j]+(Wj的后八位)pot(Wi的后八位)}————A
则
dp[i]=max(dp[i] ,ds[x][y]+((x pot a)<<8));
其中j代表祖先节点,i代表当前节点,a代表Wi的前八位,通过遍历0~255相当于遍历了所有的祖先节点的前八位,而当碰到祖先节点时就会进行更新dp[i] 的操作
在更新完dp[i]之后再更新ds[x][y]具体看代码,当然有可能对于一个结点来说ds[x][y]更新取得的值并不是这个结点的祖先的值,这个问题可以因为给出的图是树形结构而比较方便解决
总体来说就是用权值前八位即0~255的遍历代替了对于每一个祖先结点的遍历,让复杂度下降到(28n)然后就过题了
再具体一点就是说ds[x][y]记录的是所有的祖先结点的前八位为x时A式的最大值,这就省去了几个前八位同时为x时节点之间的比较
最后说一句,膜膜膜膜膜膜膜大神
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <map> #include <math.h> using namespace std; const int MOD = 1e9 + 7; const int N=1<<17; typedef long long ll; int t,n,op,vis[300],cnt; ll w[N],dx[300][300],unuse[N][300],ans; char ss[10]; std::vector<ll>g[N]; ll opt(ll a,ll b){ if(op==1) return a^b; if(op==2) return a&b; if(op==3) return a|b; } void dfs(ll u){ ll a=w[u]>>8,b=w[u]&255,dp=0; for(ll i=0;i<256;i++) if(vis[i]) dp=max(dp,dx[i][b]+(opt(a,i)<<8));//vis是为了记录当前节点的祖先中是否有前八位为i的 ans=ans+(u*((dp+w[u])%MOD))%MOD; ans%=MOD; //cout<<ans<<endl; vis[a]++;//接下来要访问u的字数所以vis[a]++ for(ll i=0;i<256;i++){ unuse[u][i]=dx[a][i]; //将访问u之前的dx[a][i]记录下来,当u的子树访问完之后还原以消除u的影响 dx[a][i]=max(dx[a][i],dp+opt(b,i));//对u的子树更新dx } for(int i=0;i<g[u].size();i++){ ll v=g[u][i]; dfs(v); } for(int i=0;i<256;i++) dx[a][i]=unuse[u][i];//还原dx vis[a]--;//表示u已经访问完它的前八位的vis减掉 } int main() { //freopen("in.txt","r",stdin); cin>>t; while(t--){ for(int i=0;i<N;i++) g[i].clear(); memset(vis,0,sizeof(vis)); memset(dx,0,sizeof(dx)); scanf("%d",&n); getchar(); scanf("%s",ss); // cout<<ss<<endl; if(ss[0]=='X') op=1; if(ss[0]=='A') op=2; if(ss[0]=='O') op=3; int i; for(i=1 ;i<=n; i++) scanf("%lld",&w[i]); for(i=2;i<=n;i++){ ll j; scanf("%lld",&j); g[j].push_back(i); } ans=0; // cout<<"r"<<endl; dfs(1); cout<<ans<<endl; } return 0; }
ancestor of imax{dp(j)+wi and wj}. 然后, 显然dp(j)+wi and wjdp(j)+w_i ext{ and }w_jdp(j)+wi and wj这个式子可以拆成dp(j)dp(j)dp(j)+[wiw_iwi后8位] and [wjw_jwj后8位] + ([wiw_iwi前8位] and [wjw_jwj前8位]) << 8.
最后求S
S.
2≤n≤216,0≤wi<216.
2≤n≤216,0≤wi<216.