zoukankan      html  css  js  c++  java
  • 【UR #2】树上GCD

    这道题是有根树点分治+烧脑的容斥+神奇的分块

    因为是规定1为根,还要求LCA,所以我们不能像在无根树上那样随便浪了,必须规定父亲,并作特殊讨论

    因为gcd并不好求,所以我们用容斥转化一下,求x为gcd的因数的个数,这样就可以随便统计了,个人觉得代码比题解要好懂。

    又因为统计完重心的所有子树,还有重心的父亲,所以在这个分治块内沿着重心的父亲一路向上爬,这时候重心的子树到重心的父亲的距离是变的,所以我们用神奇的分块大法,分类讨论,$≤sqrt{n}$使用数组记录答案,方便以后再用到的时候统计,$>sqrt{n}$时直接暴力统计,因为此时统计的复杂度并不高。这样使时间复杂度空降为$O(nsqrt{n})$。个人感觉题解说得太含糊了,一切都不如直接看代码明晰,或者说题解是帮助看懂代码的233

    这道题细节太多了,跪了1天半终于AC了蛤蛤蛤蛤蛤蛤蛤

    这道题我先膜了鏼爷的代码,因为各种指针看不懂啊,但学习了非递归求重心的新姿势,无限仰膜Orz!!!SDOI就是要练就各种非递归能力

    然后我又膜了ShallWe的题解,学会了容斥统计的方法,无限仰膜啊!!!巧妙的特判和两个指针的移动使得容斥统计不重不漏,真是太神奇了!!!

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N = 200003;
    void read(int &k) {
    	k = 0; int fh = 1; char c = getchar();
    	for(; c < '0' || c > '9'; c = getchar())
    		if (c == '-') fh = -1;
    	for(; c >= '0' && c <= '9'; c = getchar())
    		k = (k << 1) + (k << 3) + c - '0';
    	k = k * fh;
    }
    
    bool vis[N];
    struct node {
    	int nxt, to;
    } E[N];
    int qu[N], ct[N], n, m, fa[N], de, deep[N], cnt = 0, point[N], root, sz[N], boo[N];
    ll t[503][503], cn[N], sct[N], scn[N], ans2[N], S[N];
    
    void ins(int x, int y) {E[++cnt].nxt = point[x]; E[cnt].to =  y; point[x] = cnt;}
    void findrt(int x) {
    	int p = 0, q = 0; qu[++q] = x;
    	while (p != q) {
    		int u = qu[++p];
    		boo[u] = sz[u] = 1;
    		for(int tmp = point[u]; tmp; tmp = E[tmp].nxt)
    			if (!vis[E[tmp].to])
    				qu[++q] = E[tmp].to;
    	}
    	for(int i = q; i; --i) {
    		if (boo[qu[i]] && sz[qu[i]] * 2 >= q) {root = qu[i]; return;}
    		sz[fa[qu[i]]] += sz[qu[i]];
    		if (sz[qu[i]] * 2 >= q)
    			boo[fa[qu[i]]] = 0;
    	}
    }//非递归找重心!!!国家队rank3的鏼爷Orz给SDOIers带来福利
    
    void BFS(int x) {
    	deep[x] = 1;
    	int p = 0, q = 0; qu[++q] = x;
    	while (p != q) {
    		int u = qu[++p]; ++ct[deep[u]];
    		for(int tmp = point[u]; tmp; tmp = E[tmp].nxt)
    			if (!vis[E[tmp].to])
    				deep[E[tmp].to] = deep[u] + 1, qu[++q] = E[tmp].to;
    	}
    	de = deep[qu[q]];
    }
    
    void Q(int x) {
    	vis[x] = 1;
    	int up = 0, upp = 0;
    	for(int tmp = point[x]; tmp; tmp = E[tmp].nxt)
    		if (!vis[E[tmp].to]) {
    			BFS(E[tmp].to);
    			up = max(up, de);
    			for(int i = 1; i <= de; ++i)
    				for(int j = i; j <= de; j += i)
    					cn[i] += ct[j];
    			for(int i = 1; i <= de; ++i) {
    				ans2[i] += ct[i];
    				sct[i] += ct[i];
    				S[i] += scn[i] * cn[i];
    				scn[i] += cn[i];
    				ct[i] = cn[i] = 0;
    			}
    		}
    	
    	sct[0] = 1;
    	int step = 0, son = x, line, to;
    	for(int i = fa[x]; !vis[i] && i; son = i, i = fa[i]) {
    		++step; to = 0;
    		for(int tmp = point[i]; tmp; tmp = E[tmp].nxt)
    			if (!vis[E[tmp].to] && E[tmp].to != son)
    				BFS(E[tmp].to), to = max(to, de);
    		de = to;
    		upp = max(upp, de);
    		for(int j = 1; j <= de; ++j)
    			for(int k = j; k <= de; k += j)
    				cn[j] += ct[k];
    		line = min(de, m);
    		for(int j = 1; j <= line; ++j) {
    			to = step % j;
    			if (t[j][to] == -1) {
    				t[j][to] = 0;
    				for(int k = (j - to) % j; k <= up; k += j)
    					t[j][to] += sct[k];
    			}
    			S[j] += t[j][to] * cn[j];
    		}
    		for(int j = m + 1; j <= de; ++j)
    			for(int k = (j - step % j) % j; k <= up; k += j)
    				S[j] += sct[k] * cn[j];
    		for(int j = 1; j <= de; ++j)
    			ct[j] = cn[j] = 0;
    		++ans2[step];
    	}
    	
    	//下面是向ShallWe学的
    	int L = 1, R = 0, tot = step + up;
    	ll now = 0;
    	for(int i = 2; i <= tot; ++i) {
    		if (R + 1 < i) now += sct[++R];
    		if (L + step < i) now -= sct[L++];
    		ans2[i] += now;
    	}
    	//仰膜上方ShallWe的代码
    	
    	line = min(m, upp);
    	for(int i = 1; i <= line; ++i)
    		for(int j = 0; j < i; ++j)
    			t[i][j] = -1;
    	for(int i = 0; i <= up; ++i)
    		sct[i] = scn[i] = 0;
    	
    	if (son != x) {
    		findrt(son);
    		Q(root);
    	}
    	for(int tmp = point[x]; tmp; tmp = E[tmp].nxt)
    		if (!vis[E[tmp].to]) {
    			findrt(E[tmp].to);
    			Q(root);
    		}
    }
    
    ll ans[N];
    int main() {
    	read(n); m = (sqrt(n));
    	for(int i = 2; i <= n; ++i) {
    		read(fa[i]);
    		ins(fa[i], i);
    	}
    	
    	memset(t, -1, sizeof(t));
    	findrt(1);
    	Q(root);
    	for(int i = n - 1; i; --i)
    		for(int j = i + i; j < n; j += i)
    			S[i] -= S[j];
    	for(int i = 1; i < n; ++i)
    		printf("%lld
    ", S[i] + ans2[i]);	
    	
    	return 0;
    }
    

    _(:з」∠)_

  • 相关阅读:
    入坑C++之vs 新建C++项目
    入坑C++
    Solidity属性和方法的访问权限
    Solidity构造函数和析构函数
    网络直播应成为价值出口(人民时评)
    CODEVS 1203 判断浮点数是否相等
    CODEVS 1203 判断浮点数是否相等
    如何快速高效简洁的打开软件 干净利索的windows快捷程序启动器
    如何快速高效简洁的打开软件 干净利索的windows快捷程序启动器
    2018年开源状况:代码贡献超310亿行,而漏洞超16000个
  • 原文地址:https://www.cnblogs.com/abclzr/p/5473424.html
Copyright © 2011-2022 走看看