中石油训练赛 - Russian Dolls on the Christmas Tree(树上启发式合并/主席树)
題目鏈接:點擊查看
題目大意:給出一棵 n 個節點的樹,以點 1 為根,現在對于每個節點作為根的子樹求解:子樹中有多少個編號不相交的連續子段,如:1 2 4 5 7,共有三個連續的段,分別為 [ 1 , 2 ] , [ 4 , 5 ] , [ 7 ]
題目分析:樹上啟發式合并的模板題,cal 函數中直接維護一個數組用來統計加入或刪除掉一個數字后對于貢獻的影響即可:
刪除的話正好反過來
然后,今下午在和 zx 學長閑聊的時候,意外發現這個題目可以用主席樹亂搞,因為子樹對應的剛好是 dfs 序,區間內有多少個不相交的連續子段也可以用線段樹的區間合并來解決,興致勃勃來到電腦前面實現,卻發現了些許問題:
于是可持久化線段樹的區間合并就沒辦法了,也可能是我知識淺薄不會實現,抱著試一試的心態去百度了一下題解,發現這個題目真的可以用主席樹來實現,只不過需要轉換一下模型:
對于每個數字 x 來說,假設其只與前驅,也就是 x - 1 有關聯,再假設當前如果有 num 個數,如果其中有 cnt 個數的前驅也在這 num 個數當中,那么這 cnt 個數都可以和前驅合并,對答案不做貢獻,所以最后的不相交連續子段的個數是 num - cnt 個
這樣問題就轉換為了:對于某個節點來說,其子樹中有多少個節點的前驅也在子樹中,答案就是子樹的大小與這個做差了
代碼:
樹上啟發式合并
#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)//樹鏈剖分跑出重鏈 {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)//對于每個節點計算其子樹的貢獻 {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)//啟發式合并 {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; }主席樹
#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; /*主席樹*/ 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]:目標區間,[L,R]:當前區間 {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); } /*主席樹*/ /*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序建樹 {root[i]=root[i-1];update(L[id[i]-1],root[i],1,n);//第i個dfs序表示的節點是id[i],其前驅為id[i]-1,將其前驅的dfs序標記一下}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));//查找時只需要查找子樹中的點的前驅個數即可puts("");}return 0; }?
總結
以上是生活随笔為你收集整理的中石油训练赛 - Russian Dolls on the Christmas Tree(树上启发式合并/主席树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中石油训练赛 - Check List(
- 下一篇: 中石油训练赛 - Gone Fishin