树分治---点分治(挑战p360)
poj1741
题:http://poj.org/problem?id=1741
题意:给定树,和一个树k,问有多少个点对之间的路径是不超过k
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const ll INF=1e18; int ans=0,tot,maxx,root; const int M=1e4+4; struct node{ int v,w,nextt; }e[M<<1]; int head[M],vis[M],sz[M],maxv[M],num,dis[M],n,k; void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; } void dfssz(int u,int f){ sz[u]=1; maxv[u]=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v;//cout<<"!!"<<endl; if(v==f||vis[v]) continue; dfssz(v,u); sz[u]+=sz[v]; maxv[u]=max(maxv[u],sz[v]); } } void dfsroot(int r,int u,int f){ maxv[u]=max(maxv[u],sz[r]-sz[u]); if(maxv[u]<maxx){ maxx=maxv[u]; root=u; } for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(vis[v]||v==f) continue; dfsroot(r,v,u); } } void dfsdis(int u,int d,int f){ dis[num++]=d; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v==f||vis[v]) continue; dfsdis(v,d+e[i].w,u); } } int cal(int u,int d){ int sum=0; num=0; dfsdis(u,d,-1); sort(dis,dis+num); int i=0,j=num-1; while(i<j){ while(dis[i]+dis[j]>k&&i<j){ j--; } sum+=j-i; i++; } return sum; } void solve(int u){ maxx=n; dfssz(u,-1); dfsroot(u,u,-1); ans+=cal(root,0); vis[root]=1; for(int i=head[root];~i;i=e[i].nextt){ int v=e[i].v; if(vis[v]) continue; ans-=cal(v,e[i].w); solve(v); } } int main(){ while(~scanf("%d%d",&n,&k)&&(n+k)){ tot=0,ans=0; memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); for(int i=1;i<n;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); } solve(1); printf("%lld\n",ans); } return 0; }
poj1987
题:http://poj.org/problem?id=1987
和上面一样的题意,不一样的写法
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<algorithm> #include<cmath> using namespace std; const int M=2e5+5; const int inf=0x3f3f3f3f; typedef long long ll; char s[2]; ll ans; int num,n,m; ll sz[M],maxv[M],dis[M],k; int maxx,vis[M],head[M],reco,total,tot,root; struct node{ int v,nextt; ll w; }e[M<<1]; void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; } void addedge(int u,int v){ e[tot].v=v; e[tot].nextt=head[u]; head[u]=tot++; } void dfsroot(int u,int f){ sz[u]=1; ll maxson=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ dfsroot(v,u); sz[u]+=sz[v]; maxson=max(maxson,sz[v]); } } maxson=max(maxson,total-sz[u]); if(maxson<reco) root=u,reco=maxson; } void dfsdis(int u,int f,ll d){ dis[num++]=d; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v==f||vis[v]) continue; dfsdis(v,u,d+e[i].w); } } ll cal(int u,int f,ll d){ ll sum=0; num=0; dfsdis(u,f,d); sort(dis,dis+num); int i=0,j=num-1; while(i<j){ while(dis[i]+dis[j]>k&&i<j){ j--; } sum+=1ll*(j-i); i++; } return sum; } void doit(int u,int f){ ///跨越u的路径和在同一个子树中的路径 ans+=cal(u,f,0); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; ///因为同一个子树中的路径在dfs下去的时候会被算到,所以要减去被预先算过的 if(v!=f&&!vis[v]){ ans-=cal(v,u,e[i].w); } } } void solve(int u,int f){ vis[u]=1; doit(u,f); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ total=sz[v]; reco=inf; dfsroot(v,u); solve(root,0); } } } int main(){ scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); while(m--){ int u,v; ll w; scanf("%d%d%lld%s",&u,&v,&w,s); addedge(u,v,w); addedge(v,u,w); } scanf("%lld",&k); total=n; reco=inf; dfsroot(1,0); solve(root,0); printf("%lld\n",ans); return 0; }
题:https://www.luogu.org/problem/P4149
题意:给定固定路径长,边数最少路径
#include<bits/stdc++.h> using namespace std; const int M=2e5+5; const int inf=0x3f3f3f3f; const int N=1e6+6; int vis[M],temp[N],sz[M],head[M]; int n,k,total,reco,root; int ans,tot; #define pb push_back struct node{ int u,v,w,nextt; }e[M<<1]; void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; } void dfsroot(int u,int f){ int maxx=0; sz[u]=1; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ dfsroot(v,u); sz[u]+=sz[v]; maxx=max(maxx,sz[v]); } } maxx=max(maxx,total-sz[u]); if(reco>maxx) reco=maxx,root=u; } void cal(int u,int f,int d,int num){ if(d>k) return ; ans=min(ans,temp[k-d]+num); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ cal(v,u,d+e[i].w,num+1); } } } void update(int u,int f,int d,int num){ if(d>k) return ; temp[d]=min(temp[d],num); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ update(v,u,d+e[i].w,num+1); } } } void Clear(int u,int f,int d){ if(d>=k) return ; temp[d]=inf; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ Clear(v,u,d+e[i].w); } } } void dfs1(int u,int f){ sz[u]=1; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ dfs1(v,u); } } } void doit(int u,int f){ dfs1(u,f); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ cal(v,u,e[i].w,1); update(v,u,e[i].w,1); } } Clear(u,f,0); } void solve(int u,int f){ vis[u]=1; temp[0]=0; doit(u,f); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ total=sz[v]; reco=inf; dfsroot(v,u); solve(root,0); } } } int main(){ scanf("%d%d",&n,&k); for(int i=0;i<=n;i++) head[i]=-1; for(int i=0;i<=k;i++) temp[i]=inf; for(int u,v,w,i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); u++,v++; addedge(u,v,w); addedge(v,u,w); } total=n; reco=inf; ans=inf; dfsroot(1,0); solve(root,0); if(ans==inf) puts("-1"); else printf("%d\n",ans); return 0; }
题(求点权处理的题):http://acm.hdu.edu.cn/showproblem.php?pid=4812
题意:给定一棵 n 个点的树,每个点有权值 Vi,问是否存在一条路径使得路径上所有点的权值乘积 mod(10^6 + 3) 为 K,输出路径的首尾标号,若有多解,输出字典序最小的解
注意:更新就更新以重点为起点的路径,枚举答案时则用以重点孩子为起点的路径
除法,逆元处理
#pragma comment(linker,"/STACK:102400000,102400000") #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> using namespace std; #define pb push_back typedef long long ll; const int M=1e5+5; const int mod=1e6+3; const int inf=0x3f3f3f3f; int reco,total,root,n; ll k; ll sz[M],a[M],vis[M]; int ansi,ansj; int temp[mod+6]; struct node{ int v,nextt; }e[M<<1]; int head[M]; int tot; int inv[mod+6]; void addedge(int u,int v){ e[tot].v=v; e[tot].nextt=head[u]; head[u]=tot++; } void iniInv(){ inv[1]=1; for(int i=2;i<mod;i++) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod; } void dfsroot(int u,int f){ sz[u]=1; ll maxson=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ dfsroot(v,u); sz[u]+=sz[v]; maxson=max(maxson,sz[v]); } } maxson=max(maxson,total-sz[u]); if(maxson<reco) root=u,reco=maxson; } void up(int x,int y){ int minn=min(x,y); int maxx=max(x,y); if(minn<ansi){ ansi=minn,ansj=maxx; } else if(minn==ansi){ if(maxx<ansj) ansj=maxx; } } void cal(int u,int f,int d){ int x=temp[k*inv[d]%mod]; if(x) up(u,x); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ cal(v,u,d*a[v]%mod); } } } void update(int u,int f,int d){ if(!temp[d]) temp[d]=u; else temp[d]=min(temp[d],u); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ update(v,u,d*a[v]%mod); } } } void del(int u,int f,int d){ temp[d]=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ del(v,u,d*a[v]%mod); } } } void doit(int u,int f){ for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]){ cal(v,u,a[v]); update(v,u,a[u]*a[v]%mod); } } for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]){ del(v,u,a[u]*a[v]%mod); } } } void solve(int u,int f){ vis[u]=1; temp[a[u]]=u; doit(u,f); temp[a[u]]=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&v!=f){ reco=inf; total=sz[v]; dfsroot(v,u); solve(root,0); } } } int main(){ // freopen("in","r",stdin); iniInv(); while(scanf("%d%lld",&n,&k)!=EOF){ tot=0; memset(head,-1,sizeof(head)); for(int i=1;i<=n;i++) scanf("%d",&a[i]),vis[i]=0; memset(temp,0,sizeof(temp)); for(int u,v,i=1;i<n;i++){ scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } ansi=ansj=inf; reco=inf; total=n; dfsroot(1,0); solve(root,0); if(ansi==inf){ puts("No solution"); } else{ printf("%d %d\n",ansi,ansj); } } return 0; }
bzoj 2152
题:https://www.lydsy.com/JudgeOnline/problem.php?id=2152
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int M=2e4+4; struct node{ int v,nextt; int w; }e[M<<1]; int head[M],dis[4],sz[M],maxv[M],vis[M],n,tot,fenzi,maxx,root; void addedge(int u,int v,int w){ e[tot].v=v; e[tot].nextt=head[u]; e[tot].w=w; head[u]=tot++; } void dfssz(int u,int f){ maxv[u]=0; sz[u]=1; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v==f||vis[v]) continue; dfssz(v,u); sz[u]+=sz[v]; maxv[u]=max(maxv[u],sz[v]); } } void dfsroot(int r,int u,int f){ maxv[u]=max(maxv[u],sz[r]-sz[u]); if(maxx>maxv[u]){ maxx=maxv[u]; root=u; } for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v==f||vis[v]) continue; dfsroot(r,v,u); } } void dfsdis(int u,int f,int d){ dis[d%3]++; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(vis[v]||v==f) continue; dfsdis(v,u,(d+e[i].w)%3); } } ll cal(int u,int d){ dis[0]=dis[1]=dis[2]=0; dfsdis(u,-1,d); ll sum=0; for(int i=0;i<3;i++) for(int j=0;j<3;j++) if((i+j)%3==0) sum+=dis[i]*dis[j]; return sum; } void solve(int u){ maxx=n; dfssz(u,-1); dfsroot(u,u,-1); fenzi+=cal(root,0); vis[root]=1; for(int i=head[root];~i;i=e[i].nextt){ int v=e[i].v; if(vis[v]) continue; fenzi-=cal(v,e[i].w); solve(v); } } int main(){ scanf("%d",&n); memset(head,-1,sizeof(head)); for(int i=1;i<n;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); } solve(1); int fenmu=n*n; //cout<<fenmu<<endl; int g=__gcd(fenzi,fenmu); printf("%d/%d",fenzi/g,fenmu/g); return 0; }
题:https://ac.nowcoder.com/acm/contest/1099/I
题意:找出路径和为2019倍数的路径
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<map> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const ll INF=1e18; ll ans=0; int tot,maxx,root; const int M=2e4+5; struct node{ int v,w,nextt; }e[M<<1]; int head[M],vis[M],sz[M],maxv[M],dis[2020],n; void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; } void dfssz(int u,int f){ sz[u]=1; maxv[u]=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v;//cout<<"!!"<<endl; if(v==f||vis[v]) continue; dfssz(v,u); sz[u]+=sz[v]; maxv[u]=max(maxv[u],sz[v]); } } void dfsroot(int r,int u,int f){ maxv[u]=max(maxv[u],sz[r]-sz[u]); if(maxv[u]<maxx){ maxx=maxv[u]; root=u; } for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(vis[v]||v==f) continue; dfsroot(r,v,u); } } void dfsdis(int u,int d,int f){ dis[d%2019]++; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v==f||vis[v]) continue; dfsdis(v,(d+e[i].w)%2019,u); } } ll cal(int u,int d){ ll sum=0; for(int i=0;i<=2019;i++) dis[i]=0; dfsdis(u,d,-1); for(int i=0;i<2019;i++){ if(dis[i]==0||dis[(2019-i)%2019]==0) continue; sum+=1ll*dis[i]*(dis[(2019-i)%2019]); } // mp.clear(); return sum; } void solve(int u){ maxx=n; dfssz(u,-1); dfsroot(u,u,-1); ans+=cal(root,0); vis[root]=1; for(int i=head[root];~i;i=e[i].nextt){ int v=e[i].v; if(vis[v]) continue; ans-=cal(v,e[i].w); solve(v); } } int main(){ while(~scanf("%d",&n)){ tot=0,ans=0ll; memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); for(int i=1;i<n;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); } solve(1); printf("%lld\n",(ans-n)/2);//要减去自己到自己的贡献 } return 0; }
点分治好题:https://www.luogu.org/problem/P2664
最终思考于:https://blog.csdn.net/wu_tongtong/article/details/78963542
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int M=2e5+5; int reco,total; ll sz[M]; int head[M],C[M],vis[M],cnt[M],maxv[M],n,tot,root,num,sum,tt; ll ans[M],col[M]; struct node{ int v,nextt; }e[M<<1]; void addedge(int u,int v){ e[tot].v=v; e[tot].nextt=head[u]; head[u]=tot++; } void dfsroot(int u,int f){ sz[u]=1; ll maxson=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ dfsroot(v,u); sz[u]+=sz[v]; maxson=max(maxson,sz[v]); } } maxson=max(maxson,total-sz[u]); if(maxson<reco) root=u,reco=maxson; } void dfs1(int u,int f){ sz[u]=1; cnt[C[u]]++; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ dfs1(v,u); sz[u]+=sz[v]; } } if(cnt[C[u]]==1){ sum+=sz[u]; col[C[u]]+=sz[u]; } cnt[C[u]]--; } void change(int u,int f,int sign){ cnt[C[u]]++; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]) change(v,u,sign); } if(cnt[C[u]]==1){ sum+=1ll*sign*sz[u]; col[C[u]]+=1ll*sign*sz[u]; } cnt[C[u]]--; } void dfs2(int u,int f){ cnt[C[u]]++; if(cnt[C[u]]==1){ sum-=col[C[u]]; num++; } ans[u]+=1ll*sum+num*tt; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ dfs2(v,u); } } if(cnt[C[u]]==1){ sum+=col[C[u]]; num--; } cnt[C[u]]--; } void cle(int u,int f){ cnt[C[u]]=0; col[C[u]]=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]) cle(v,u); } } void doit(int u,int f){ dfs1(u,f); ans[u]+=(ll)sum; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ cnt[C[u]]++; col[C[u]]-=sz[v]; sum-=sz[v]; change(v,u,-1); cnt[C[u]]--; tt=sz[u]-sz[v]; dfs2(v,u); cnt[C[u]]++; col[C[u]]+=sz[v]; sum+=sz[v]; change(v,u,1); cnt[C[u]]--; } } sum=num=0; cle(u,f); } void solve(int u,int f){ vis[u]=1; doit(u,f); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=f&&!vis[v]){ total=sz[v]; reco=inf; dfsroot(v,u); solve(root,0); } } } int main(){ scanf("%d",&n); for(int i=0;i<=n;i++) head[i]=-1; for(int i=1;i<=n;i++) scanf("%d",&C[i]); for(int u,v,i=1;i<n;i++){ scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } total=n; reco=inf; dfsroot(1,0); solve(root,0); for(int i=1;i<=n;i++) printf("%lld\n",ans[i]); return 0; }