中石油训练赛 - Russian Dolls on the Christmas Tree(树上启发式合并/主席树)
題目鏈接:點(diǎn)擊查看
題目大意:給出一棵 n 個(gè)節(jié)點(diǎn)的樹(shù),以點(diǎn) 1 為根,現(xiàn)在對(duì)于每個(gè)節(jié)點(diǎn)作為根的子樹(shù)求解:子樹(shù)中有多少個(gè)編號(hào)不相交的連續(xù)子段,如:1 2 4 5 7,共有三個(gè)連續(xù)的段,分別為 [ 1 , 2 ] , [ 4 , 5 ] , [ 7 ]
題目分析:樹(shù)上啟發(fā)式合并的模板題,cal 函數(shù)中直接維護(hù)一個(gè)數(shù)組用來(lái)統(tǒng)計(jì)加入或刪除掉一個(gè)數(shù)字后對(duì)于貢獻(xiàn)的影響即可:
刪除的話(huà)正好反過(guò)來(lái)
然后,今下午在和 zx 學(xué)長(zhǎng)閑聊的時(shí)候,意外發(fā)現(xiàn)這個(gè)題目可以用主席樹(shù)亂搞,因?yàn)樽訕?shù)對(duì)應(yīng)的剛好是 dfs 序,區(qū)間內(nèi)有多少個(gè)不相交的連續(xù)子段也可以用線(xiàn)段樹(shù)的區(qū)間合并來(lái)解決,興致勃勃來(lái)到電腦前面實(shí)現(xiàn),卻發(fā)現(xiàn)了些許問(wèn)題:
于是可持久化線(xiàn)段樹(shù)的區(qū)間合并就沒(méi)辦法了,也可能是我知識(shí)淺薄不會(huì)實(shí)現(xiàn),抱著試一試的心態(tài)去百度了一下題解,發(fā)現(xiàn)這個(gè)題目真的可以用主席樹(shù)來(lái)實(shí)現(xiàn),只不過(guò)需要轉(zhuǎn)換一下模型:
對(duì)于每個(gè)數(shù)字 x 來(lái)說(shuō),假設(shè)其只與前驅(qū),也就是 x - 1 有關(guān)聯(lián),再假設(shè)當(dāng)前如果有 num 個(gè)數(shù),如果其中有 cnt 個(gè)數(shù)的前驅(qū)也在這 num 個(gè)數(shù)當(dāng)中,那么這 cnt 個(gè)數(shù)都可以和前驅(qū)合并,對(duì)答案不做貢獻(xiàn),所以最后的不相交連續(xù)子段的個(gè)數(shù)是 num - cnt 個(gè)
這樣問(wèn)題就轉(zhuǎn)換為了:對(duì)于某個(gè)節(jié)點(diǎn)來(lái)說(shuō),其子樹(shù)中有多少個(gè)節(jié)點(diǎn)的前驅(qū)也在子樹(shù)中,答案就是子樹(shù)的大小與這個(gè)做差了
代碼:
樹(shù)上啟發(fā)式合并
#pragma GCC optimize(2) #pragma GCC optimize("Ofast","inline","-ffast-math") #pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=2e5+100;vector<int>node[N];int ans[N],son[N],num[N],sum;bool vis[N],book[N];void dfs_son(int u,int fa)//樹(shù)鏈剖分跑出重鏈 {son[u]=-1;num[u]=1;for(auto v:node[u]){if(v==fa)continue;dfs_son(v,u);num[u]+=num[v];if(son[u]==-1||num[v]>num[son[u]])son[u]=v;} }void cal(int u,int fa,int val)//對(duì)于每個(gè)節(jié)點(diǎn)計(jì)算其子樹(shù)的貢獻(xiàn) {if(val==1){book[u]=true;if(book[u-1]&&book[u+1])sum--;else if(book[u-1]||book[u+1]);elsesum++;}else{book[u]=false;if(book[u-1]&&book[u+1])sum++;else if(book[u-1]||book[u+1]);elsesum--;}for(auto v:node[u]){if(v==fa||vis[v])continue;cal(v,u,val);} }void dfs(int u,int fa,int keep)//啟發(fā)式合并 {for(auto v:node[u]){if(v==fa||v==son[u])continue;dfs(v,u,0);}if(son[u]!=-1){dfs(son[u],u,1);vis[son[u]]=true;}cal(u,fa,1);ans[u]=sum;if(son[u]!=-1)vis[son[u]]=false;if(!keep)cal(u,fa,-1); }void init(int n) {for(int i=1;i<=n;i++)node[i].clear();memset(vis,false,n+5);memset(book,false,n+5);sum=0; }int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);int w;cin>>w;int kase=0;while(w--){int n;scanf("%d",&n);init(n);for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);node[u].push_back(v);node[v].push_back(u);}dfs_son(1,-1);dfs(1,-1,1);printf("Case #%d:",++kase);for(int i=1;i<=n;i++)printf(" %d",ans[i]);puts("");}return 0; }主席樹(shù)
#pragma GCC optimize(2) #pragma GCC optimize("Ofast","inline","-ffast-math") #pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=2e5+100; /*主席樹(shù)*/ struct Node {int l,r;int sum; }tree[N*20];int cnt,root[N];void update(int pos,int &k,int l,int r) {if(pos<l||pos>r)return;tree[cnt++]=tree[k];k=cnt-1;tree[k].sum++;if(l==r)return;int mid=l+r>>1;if(pos<=mid)update(pos,tree[k].l,l,mid);elseupdate(pos,tree[k].r,mid+1,r); }int query(int i,int j,int l,int r,int L,int R)//[l,r]:目標(biāo)區(qū)間,[L,R]:當(dāng)前區(qū)間 {if(R<l||L>r)return 0;if(L>=l&&R<=r)return tree[j].sum-tree[i].sum;int mid=L+R>>1;return query(tree[i].l,tree[j].l,l,r,L,mid)+query(tree[i].r,tree[j].r,l,r,mid+1,R); } /*主席樹(shù)*/ /*dfs序*/ vector<int>node[N];int L[N],R[N],tot,id[N],sz[N]; void dfs(int u,int fa) {sz[u]=1;L[u]=++tot;id[tot]=u;for(auto v:node[u]){if(v==fa)continue;dfs(v,u);sz[u]+=sz[v];}R[u]=tot; } /*dfs序*/ void init(int n) {root[0]=0;tree[0].l=tree[0].r=tree[0].sum=0;cnt=1;tot=0;for(int i=1;i<=n;i++)node[i].clear(); }int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);int w;cin>>w;int kase=0;while(w--){int n;scanf("%d",&n);init(n);for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);node[u].push_back(v);node[v].push_back(u);}dfs(1,-1);for(int i=1;i<=n;i++)//遍歷dfs序建樹(shù) {root[i]=root[i-1];update(L[id[i]-1],root[i],1,n);//第i個(gè)dfs序表示的節(jié)點(diǎn)是id[i],其前驅(qū)為id[i]-1,將其前驅(qū)的dfs序標(biāo)記一下}printf("Case #%d:",++kase);for(int i=1;i<=n;i++)printf(" %d",sz[i]-query(root[L[i]-1],root[R[i]],L[i],R[i],1,n));//查找時(shí)只需要查找子樹(shù)中的點(diǎn)的前驅(qū)個(gè)數(shù)即可puts("");}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的中石油训练赛 - Russian Dolls on the Christmas Tree(树上启发式合并/主席树)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 中石油训练赛 - Check List(
- 下一篇: 中石油训练赛 - Gone Fishin