[luogu2664]树上游戏
生活随笔
收集整理的這篇文章主要介紹了
[luogu2664]树上游戏
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言
點分治
題目相關
鏈接
題目大意
給出一棵樹,每個節點有一個顏色
設s(u,v)s(u,v)s(u,v)為樹上點uuu到點vvv的路徑上的顏色數
對于每個iii,求Si=∑j=1ns(i,j)S_i=\sum_{j=1}^ns(i,j)Si?=j=1∑n?s(i,j)
數據范圍
n≤100000n\le100000n≤100000
題解
點分治大法好(但是我們這里不考慮)
考慮每種顏色的貢獻
我們對于一種顏色把所有點拿出來
我們發現這些點將整棵樹切成了若干聯通塊(即不是這個顏色的點的聯通快)
我們發現對于在同一塊內的兩個點為端點的路徑是不包含這種顏色的,其它路徑都包含
那么我們現在考慮用所有的邊的方案減去不要求的方案
我們發現我們可以直接維護當前子樹內每種顏色的邊界節點,并用樹上差分維護答案的區間減
最后dfs一遍下傳差分標記即可
容易發現這些邊界節點會被加入一次再刪除一次
故總復雜度O(n)\mathcal O(n)O(n)
代碼
#include<cstdio> #include<cctype> #include<algorithm> #include<vector> namespace fast_IO {const int IN_LEN=1000000,OUT_LEN=1000000;char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}inline void flush(){fwrite(obuf,1,oh-obuf,stdout);} } using namespace fast_IO; #define getchar() getchar_() #define putchar(x) putchar_((x)) #define rg register typedef long long ll; template <typename T> inline T max(const T a,const T b){return a>b?a:b;} template <typename T> inline T min(const T a,const T b){return a<b?a:b;} template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;} template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;} template <typename T> inline T abs(const T a){return a>0?a:-a;} template <typename T> inline void Swap(T&a,T&b){T c=a;a=b;b=c;} //template <typename T> inline void swap(T*a,T*b){T c=a;a=b;b=c;} template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);} template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;} template <typename T> inline T square(const T x){return x*x;}; template <typename T> inline void read(T&x) {char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x; } template <typename T> inline void printe(const T x) {if(x>=10)printe(x/10);putchar(x%10+'0'); } template <typename T> inline void print(const T x) {if(x<0)putchar('-'),printe(-x);else printe(x); } const int maxn=100005,maxm=200005; int n,c[maxn]; int head[maxn],nxt[maxm],tow[maxm],tmp; inline void addb(const int u,const int v) {tmp++;nxt[tmp]=head[u];head[u]=tmp;tow[tmp]=v; } int size[maxn]; ll ans[maxn]; std::vector<int>col[maxn]; void dfs(const int u,const int fa) {std::vector<int>&me=col[c[u]];const int SZ=me.size();size[u]=1;for(rg int i=head[u];i;i=nxt[i]){const int v=tow[i];if(v==fa)continue;dfs(v,u),size[u]+=size[v];if(u==0){for(c[u]=1;c[u]<=100000;c[u]++){std::vector<int>&M=col[c[u]];int dq=size[v];for(int i=M.size()-1;i>=SZ;i--)dq-=size[M[i]];ans[v]-=dq;for(int i=M.size()-1;i>=SZ;i--)ans[M[i]]+=dq;while(M.size()>0)M.pop_back();}continue;}int dq=size[v];for(int i=me.size()-1;i>=SZ;i--)dq-=size[me[i]];ans[v]-=dq;for(int i=me.size()-1;i>=SZ;i--)ans[me[i]]+=dq;while(me.size()>SZ)me.pop_back();}me.push_back(u); } void cf(const int u,const int fa) {for(rg int i=head[u];i;i=nxt[i]){const int v=tow[i];if(v==fa)continue;ans[v]+=ans[u],cf(v,u);} } int main() {read(n);for(rg int i=1;i<=n;i++)read(c[i]);addb(0,1),addb(1,0);for(rg int i=1;i<n;i++){int u,v;read(u),read(v);addb(u,v),addb(v,u);}dfs(0,0);cf(0,0);for(rg int i=1;i<=n;i++)print(ans[i]+(ll)n*100000),putchar('\n');return flush(),0; }總結
點分好難,還是差分清真
總結
以上是生活随笔為你收集整理的[luogu2664]树上游戏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ZJOI2019一试翻车记
- 下一篇: 数列分块入门(套题)(loj6277,l