HDU 3065 2017 Multi-University Training Contest

来源:互联网 发布:录屏软件cs6 编辑:程序博客网 时间:2024/05/16 11:25

题意:给定一棵树(n<=2e5),每个点都有一个颜色C(1..n)。现在定义道路u-v的value为u-v简单道路上不同颜色的数量。现在要求整棵树上所有的n*(n-1)/2条道路的value和(u-v和v-u算同一条,i-i不算)。

题解:换一种方式求总和,把每种颜色的贡献单独计算。再换一个角度,先假定所有的n*(n-1)/2条道路的value都是最大值n,然后对于1..n每种颜色,把不该算的道路数量找出来,在总答案上剪掉即可。

如果某种颜色未出现过,那么该颜色在所有道路上都被非法计算了一次,答案减掉n*(n-1)/2

如果某种颜色出现果过,那么非法统计的答案是:没有出现过该颜色的道路数量。想要找到这些道路是简单的:把所有该颜色的点删掉,此时图中剩下的所有的道路总数即是所求。我们紧接着对这个思路进行优化:删点之后,树被拆成了一个森林。而新森林的每个小树必定有一个性质:叶子节点要么是老树的叶子,要么在老树中有一条边连像删掉的点,所以每个小树,必定有是原图中某个该颜色的点的一条树枝的一部分。因此我们就是要找到所有这样的块的大小size,答案减掉size*(size-1)/2。

那么对于颜色i,我们枚举每个颜色为i的点x,x的出度(去掉指向父亲的边)就等于树枝个数。那么继续枚举x的每个儿子节点y,把y看成根,就是一个树枝,也是我们所说的一个块的初始形态,我们要在y为根的树枝上,找到所有最接近y的同样颜色为i的点p,把p这个树枝全部砍掉,这样我们就限制好了一个块。下面问题转变成了如何找到树枝y上,离y最近的几个颜色为i的点(所有y到叶子的道路上,最接近y的颜色为i的点)。

DFS序列可以满足我们的需求:我们需要构造一个虚根0,他指向树根1。DFS序本身类似一个括号序列,他一定是合法的。而每一个节点a会出现两次。第一次是遍历到a的时候,出现的位置叫做L[a],第二次是回溯的时候,出现的位置叫做R[a],那么L[a]和R[a]之间出现过的所有的点就是树枝a上的所有的点。同时,既然这是一个合法的括号序列,那么假如说a有三个树枝,括号序列(a出现的地方改成花括号表示)必然呈现出{ ()()()}的样子(小括号里面有更小的括号,省略不写)。那么要找到所有以树枝上颜色为i的点,我们可以这样进行:在颜色为i的点集合中,首先找到L中第一个比La大于等于的Lx,如果Lx<Ra说明x位于a树枝上,那么去掉所有x以下的点,然后下一次,要考虑去掉x树枝的剩下的a树枝,我们找第一个比Rx+1(这个位置是x树枝括号序列结束的下一个位置,也就是下一条树枝开始的地方或者a括号结束的地方)大于等于的Lxx,再把这条树枝去掉,然后在找到第一个比Rxx+1大于等于的Lxxx,把他去掉………………..这样我们就限制得到了一个块,同时得到他的size,这个块中的所有道路,在颜色i的贡献上都做了非法贡献,减掉size*(size-1)/2。

总结一句话就是emmmm考虑每个颜色i,找到被所有i颜色点隔开的所有的块大小size就是了。

然后体验了一波auto。。。。以及vector等用lower_bound查找到it=end指针的时候,不要输出*it,会爆炸。。。我还以为代码写错了呢。。。另外auto & 可以修改相应内存单元的值。这么简洁的一个树上块剖(姑且这么叫他)的算法被我描述的。。。。这么啰嗦orz。。。太弱辣

注意:代码里有一条注释,他在任何情况下都会导致程序崩掉。就是因为上面说的问题。

Code:

#include<bits/stdc++.h>using namespace std;#define MAX 200006vector<int> E[MAX],C[MAX];int R[MAX],L[MAX],S[MAX],F[MAX];int n;void dfs(int nod,int father,int &&count){L[nod]=++count;S[nod]=1;F[nod]=father;for (auto a:E[nod]){if ((a)==father){continue;}dfs(a,nod,move(count));S[nod]+=S[a];}R[nod]=count;}bool cmp(const int& x,const int& y){return L[x]<L[y];}int main(){int num=1;E[0].push_back(1);while (scanf("%d",&n)!=EOF){for (int i=1;i<=n;i++){E[i].clear();C[i].clear();}for (int i=1;i<=n;i++){int temp;scanf("%d",&temp);C[temp].push_back(i);}for (int i=0;i<n-1;i++){int a,b;scanf("%d%d",&a,&b);E[a].push_back(b);E[b].push_back(a);}dfs(0,0,0);long long ans = 1LL*n*n*(n-1)/2;for (int i = 1;i<=n;i++){if (C[i].empty()){ans-=1LL*n*(n-1)/2;}else{C[i].push_back(0);sort(C[i].begin(),C[i].end(),cmp);for (auto &x:C[i]){for (auto &y:E[x]){if (y==F[x]){continue;}int size = S[y];int k = L[y];while (true){L[n+1] = k;auto it = lower_bound(C[i].begin(),C[i].end(),n+1,cmp);//cout<<x<<" "<<y<<" "<<*it<<" "<<R[*it]<<endl;if (it==C[i].end()||L[(*it)]>R[y]){break;}size-=S[*it];k = R[*it]+1;}ans -= 1LL*size*(size-1)/2;}}}}cout<<"Case #"<<num++<<": "<<ans<<endl;}return 0; }


原创粉丝点击