题目:https://ac.nowcoder.com/acm/contest/296/J
可以点分治,每次处理经过重心的路径。
合法的形态有这几种:,其中 [ ] 的第一个表示小于号的个数,第二个表示大于号的个数。“2”表示有多个。如果左边是 1 、右边是 k 的话,3的合法条件是 w[1]<=w[k] , 4的合法条件是 w[1]>=w[k] 。
弄一个 f [0/1/2][0/1/2][N] 的桶,存当前重心的其他孩子里各种情况的个数; dfs 当前孩子的时候对于“重心到当前节点的路径”在桶里找一些东西匹配上更新答案,然后再 dfs 一遍当前孩子来更新桶;继续分治之前把所有孩子都 dfs 一遍清空桶(就是正常的点分治流程)。所以用树状数组实现桶。
然后开始各种转移。调了一晚上+一下午还是没调出来。严格递增与非严格递增好麻烦呀。那个第5种情况感觉有好多变种,比如 --_ 再配上一个 / 或者 -- 之类的。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=1e5+5; int T,n,hd[N],xnt,to[N<<1],nxt[N<<1],w[N],lm; int mn,rt,siz[N],f[3][3][N]; ll ans; bool vis[N]; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mx(int a,int b){return a>b?a:b;} int Mn(int a,int b){return a<b?a:b;} void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;} void init() { xnt=0;memset(hd,0,sizeof hd);ans=0;lm=0; memset(vis,0,sizeof vis); } void init_dfs(int cr,int fa) { siz[cr]=1; for(int i=hd[cr],v;i;i=nxt[i]) if((v=to[i])!=fa)init_dfs(v,cr),siz[cr]+=siz[v]; } void getrt(int cr,int fa,int s) { int mx=0,sm=0; for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa) { getrt(v,cr,s); mx=Mx(mx,siz[v]);sm+=siz[v]; } mx=Mx(mx,s-sm); if(mx<mn)mn=mx,rt=cr; } void add(int x,int k,int f[]){for(;x<=lm;x+=(x&-x))f[x]+=k;}//lm!!! int qry(int x,int f[]){if(!x)return 0;int ret=0;for(;x;x-=(x&-x))ret+=f[x];return ret;} int qry_s(int x,int f[]){return qry(lm,f)-qry(x-1,f);}//lm!!!not n void solve(int s0,int s1,int tw) { ans++;//with rt if(!s0&&!s1)// { for(int i=0;i<=2;i++)for(int j=0;j<=2;j++)ans+=qry(lm,f[i][j]); return; } ans+=qry(lm,f[0][0]);// if(!s0&&s1) { ans+=qry(lm,f[0][1]); ans+=qry(lm,f[0][2]);//1,1 ans+=qry_s(tw,f[1][2]); ans+=qry_s(tw,f[1][1]); ans+=qry_s(tw+(s1==1),f[1][0]);//1,3 //not only qry_s(tw,f[1][2]);!!!//not creat 5///if for !s0&&s1==1 ans+=qry_s(tw,f[1][1]);//1,5[2] } if(s0&&!s1) { ans+=qry(lm,f[1][0]); ans+=qry(lm,f[2][0]);//2,2 ans+=qry(tw,f[2][1]); ans+=qry(tw,f[1][1]);if(s0>1)ans+=qry(tw-(s0==1),f[0][1]);//2,4// ans+=qry(tw,f[1][1]);//2,5[1] } if(s0==1&&s1>1) { ans+=qry_s(tw,f[0][1]); ans+=qry_s(tw,f[0][2]);//3,1 } if(s0>1&&s1==1) { ans+=qry(tw,f[1][0]); ans+=qry(tw,f[2][0]);//4,2 } if(s0==1&&s1==1)//no w[rt]==tw is ok { if(w[rt]<tw){ ans+=qry(tw,f[1][0]); ans+=qry(tw,f[2][0]); }//5[1],2//back so w[rt]<tw else if(w[rt]>tw){ ans+=qry_s(tw,f[0][1]); ans+=qry_s(tw,f[0][2]); }//5[2],1 } if(!s0&&s1==1)///////////// creat 5 and others! { ans+=qry(tw,f[1][0]); ans+=qry(tw,f[2][0]);// } if(s0==1&&!s1) { ans+=qry_s(tw,f[0][1]); ans+=qry_s(tw,f[0][2]);// } } void dfs(int cr,int fa,int lst,int s0,int s1,int op) { if(op==1) { if(w[cr]>lst)s1++; else if(w[cr]<lst)s0++; }//cr_lst//back else { if(w[cr]>lst)s0++; else if(w[cr]<lst)s1++; }//lst_cr//go if(s0>1&&s1>1)return; if(s0==1&&s1>1){ if(op>1&&w[cr]<w[rt])return; if(op==1&&w[cr]>w[rt])return; } if(s0>1&&s1==1){ if(op>1&&w[cr]>w[rt])return; if(op==1&&w[cr]<w[rt])return; } if(op==1)solve(s0,s1,w[cr]),printf("cr=%d[%d,%d] ans=%lld\n",cr,s0,s1,ans); if(op==2)add(w[cr],1,f[s0>1?2:s0][s1>1?2:s1]);///can't >2 if(op==3)add(w[cr],-1,f[s0>1?2:s0][s1>1?2:s1]); for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=fa)dfs(v,cr,w[cr],s0,s1,op); } void solve(int cr,int s) { vis[cr]=1; printf("cr=%d s=%d\n",cr,s); for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]) { dfs(v,cr,w[cr],0,0,1);dfs(v,cr,w[cr],0,0,2); } printf(" ans=%lld\n",ans); for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]) { dfs(v,cr,w[cr],0,0,3); } for(int i=hd[cr],v;i;i=nxt[i]) if(!vis[v=to[i]]) { int ts;if(siz[v]<siz[cr])ts=siz[v];else ts=s-siz[cr]; mn=N;getrt(v,cr,ts);solve(rt,ts); } } int main() { T=rdn(); while(T--) { init();n=rdn();for(int i=1;i<=n;i++)w[i]=rdn(),lm=Mx(lm,w[i]); for(int i=1,u,v;i<n;i++)u=rdn(),v=rdn(),add(u,v),add(v,u); init_dfs(1,0);mn=N;getrt(1,0,n);solve(rt,n); printf("%lld\n",ans+n); } return 0; }