• 省选补题记录


    省选补题记录

    (15-20) 年的省选题挨个写了写。

    省选联考2020

    DAY1T1 冰火战士

    题意描述

    洛谷

    有两种战士,冰系战士和火系战士,每个战士有两个参数温度和能量。

    对于冰系战士,如果赛场温度不低于其自身温度,那么它可以出战。

    对于火系战士,如果赛场温度不高于其自身温度,那么它可以出战。

    对于每一个赛场温度,冰火双方消耗的能量总和为 (min { ext{能够参赛冰系战士的能量之和}, ext{能够参赛的火系战士的能量之和}} imes 2)

    现在想让你找一个最佳赛场温度使得冰火双方消耗的能量总和最大。

    (q) 次操作,每次操作分两种,新来一个温度为 (x), 能量为 (y) 的冰系/火系战士,删除第 (k) 次操作。

    每次操作之后,问你最佳赛场温度和冰火双方消耗的能量总和为多少。

    数据范围:(1leq qleq imes 2 imes 10^6,x_ileq 2 imes 10^9,sum y_ileq 10^9)

    solution

    树状数组/线段树+二分+卡常

    我们先把所有的战士按温度从小到大排序。

    不难发现有这样几个条件:

    • 冰系战士能出战的是一段前缀。
    • 火系战士能出战的是一段后缀。
    • 最佳赛场温度一定是某位战士的温度。

    我们设 (fire(i)) 表示火系战士能量的后缀和,(ice(i)) 表示冰系战士能量的前缀和。

    然后我们把 (fire(i),ice(i)) 关于时间 (i) 的函数画一下:

    这里由于温度是整数,所以函数图像不一定是连续的,这里为了好看点就化成连续的了。

    那么 (min(fire(i),ice(i))) 的函数图像就是下面的那一部分。

    不难发现两个函数图像的交点处的 (min(fire(i),ice(i))) 的取值是最大的。

    但由于其函数图像不连续,所以我们取最靠近交点的两个地方,即:

    • 最大的满足 (fire(i) > ice(i))(i)
    • 最小的满足 (ice(i)>fire(i))(i)

    然后由于 (fire(i)) 单调递减,(ice(i)) 单调递增,所以二分一下即可。

    由于每次加入/删除一个战士都要维护一下前/后缀和,随便拿树状数组一下就好了。

    复杂度:(O(nlog ^2 n))

    好像还有 (O(nlogn)) 的在树状数组上二分的做法,但我没看懂,所以就咕咕咕了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int N = 2e6+10;
    int n,fire,ice,cnt,inf,ans,pos;
    int b[N],t1[N],t2[N];
    struct node
    {
    	int opt,type,x,y;
    }q[N];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    int lowbit(int x){return x & (-x);}
    void chenge(int x,int w)
    {
    	for(; x; x -= lowbit(x)) t1[x] += w;
    }
    void modify(int x,int w)
    {
    	for(; x <= inf; x += lowbit(x)) t2[x] += w;
    }
    int ask1(int x)//火 
    {
    	int res = 0;
    	for(; x <= inf; x += lowbit(x)) res += t1[x];
    	return res;
    }
    int ask2(int x)//冰 
    {
    	int res = 0;
    	for(; x; x -= lowbit(x)) res += t2[x];
    	return res;
    }
    void Erfen()
    {
    	int L = 1, R = inf, ans1 = 0, p1 = 0;
    	while(L <= R)
    	{
    		int mid = (L + R)>>1;
    		int res1 = ask1(mid), res2 = ask2(mid);
    		if(res1 >= res2)
    		{
    			p1 = mid;
    			ans1 = res2;
    			L = mid + 1;
    		}
    		else R = mid - 1;
    	}
    	int ans2 = min(ask1(p1+1),ask2(p1+1)), p2;
    	L = p1+1, R = inf;	
    	while(L <= R)
    	{
    		int mid = (L + R)>>1;
    		int res1 = ask1(mid), res2 = ask2(mid);
    		if(min(res1,res2) == ans2)
    		{
    			p2 = mid;
    			L = mid + 1;
    		}
    		else R = mid - 1;
    	}
    	if(ans1 == ans2) ans = ans1, pos = max(p1,p2);
    	else if(ans2 > ans1) ans = ans2, pos = p2;
    	else ans = ans1, pos = p1;
    	if(ans*2 == 0) printf("Peace
    ");
    	else printf("%d %d
    ",b[pos],ans*2);
    }
    int main()
    {
    //	freopen("a.in","r",stdin);
    //	freopen("a.out","w",stdout);
    	n = read();
    	for(int i = 1; i <= n; i++)
    	{
    		q[i].opt = read();
    		if(q[i].opt == 1)
    		{
    			q[i].type = read();
    			q[i].x = read();
    			q[i].y = read();
    			b[++cnt] = q[i].x;
    		}
    		else q[i].x = read();
    	}
    	sort(b+1,b+cnt+1);
    	inf = unique(b+1,b+cnt+1)-b-1;
    	for(int i = 1; i <= n; i++) if(q[i].opt == 1) q[i].x = lower_bound(b+1,b+inf+1,q[i].x)-b;
    	for(int i = 1; i <= n; i++)
    	{
    		if(q[i].opt == 1)
    		{
    			if(q[i].type == 1) 
    			{
    				fire++;
    				chenge(q[i].x,q[i].y);
    			}
    			else
    			{
    				ice++;
    				modify(q[i].x,q[i].y);
    			}
    		}
    		else
    		{
    			int id = q[i].x;
    			if(q[id].type == 1)
    			{
    				fire--;
    				chenge(q[id].x,-q[id].y);
    			}
    			else
    			{
    				ice--;
    				modify(q[id].x,-q[id].y);
    			}
    		}
    		if(!fire || !ice) printf("Peace
    ");
    		else Erfen();
    	}
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    

    DAY1T2 组合数问题

    题意描述

    (left(displaystylesum_{k=0}^{n}f(k) imes x^k imes {nchoose k} ight) mod p)

    其中 (f(k)) 为一个给定的 (m) 次多项式 (f(k) = a_0k^0+a_1k^1+...+a_mk^m)

    数据范围:(nleq 10^9,mleq 1000)

    solution

    数论数学+斯特林数。

    我们要求的其实是:(displaystylesum_{k=0}^{n} sum_{j=0}^{m} a^jk^j imes x^k imes {nchoose k})

    后面有关于组合数的一项,所以我们考虑把多项式转化为下降幂多项式。

    推一下转化的柿子:

    根据 (m^n = displaystylesum_{i=0}^{n} egin{Bmatrix}n\iend{Bmatrix}m^{underline i}), 则有:

    (displaystylesum_{i=0}^{n} a_ix^i=displaystylesum_{i=0}^{n} a^isum_{j=0}^{i} egin{Bmatrix}i\jend{Bmatrix} x^{underline j})

    交换一下求和顺序可得:

    (displaystylesum_{i=0}^{n}a_ix^i = sum_{i=0}^{n}x^{underline i} sum_{j=i}^{n} egin{Bmatrix}i\jend{Bmatrix}a^j)

    (displaystyle b_i = sum_{j=i}^{n}egin{Bmatrix}i\jend{Bmatrix} a_j) ,那么 (displaystylesum_{i=0}^{n} a_ix^i = sum_{i=0}^{n}b_ix^{underline i})

    把这个代入原式可得:

    (displaystylesum_{k=0}^{n} sum_{j=0}^{m} a_jk^j imes x^k imes {nchoose k})

    (=displaystylesum_{k=0}^{n}sum_{j=0}^{m}b_jk^{underline j} imes x^k imes {nchoose k})

    交换一下求和顺序可得:

    (原式=displaystylesum_{j=0}^{m} b_jsum_{k=0}^{n} k^{underline j} imes x^k imes {nchoose k})

    关于组合数和下降幂有一个很好的性质:

    (displaystyle k^{underline m} imes {nchoose k} = {n-mchoose k-m} imes n^{underline m})

    具体证明如下:

    (displaystyle k^{underline m} imes {nchoose k} = {k!over (k-m)!} imes {n!over k!(n-k)!} = {n!over (k-m)! (n-k)!} = {{n!(n-m)!}over (k-m)!(n-k)!(n-m)!} = {n!over (n-m)!} {(n-m)!over (k-m)!(n-k)!} = n^{underline m} imes {n-mchoose k-m})

    把这个带回去可得:

    (原式=displaystylesum_{j=0}^{m} b_jn^{underline j}sum_{k=0}^{n} x^k imes {n-jchoose k-j})

    又因为当 (k>j) 的时候,({n-jchoose k-j} = 0), 所以第二层循环只需要枚举到 (j) 就可以了,即:

    (原式=displaystylesum_{j=0}^{m} b_j n^{underline j} sum_{k=0}^{j} x^k imes {n-jchoose k-j})

    根据二项式定理可得:({n-jchoose k-j} imes x^k imes 1^{k-j} = (1+x)^{n-j})

    带回去可得:

    (原式=displaystylesum_{j=0}^{m} b_jn^{underline j}(1+x)^{n-j})

    然后 (O(m^2)) 直接算即可。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    const int N = 5010;
    int n,x,p,m,ans;
    int jz[N],s[N][N],a[N],b[N];
    inline int read()
    {
    	int s = 0, w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    int ksm(int a,int b)
    {
    	int res = 1;
    	for(; b; b >>= 1)
    	{
    		if(b & 1) res = res * a % p;
    		a = a * a % p;
    	}
    	return res;
    }
    signed main()
    {
    	n = read(); x = read(); p = read(); m = read();
    	for(int i = 0; i <= m; i++) a[i] = read();
    	s[0][0] = jz[0] = 1; 
    	for(int i = 1; i <= m; i++) jz[i] = jz[i-1] * i % p;
    	for(int i = 1; i <= m; i++)
    	{
    		for(int j = 1; j <= m; j++)
    		{
    			s[i][j] = (s[i-1][j-1] + j * s[i-1][j]  % p) % p;
    		}
    	}
    	for(int i = 0; i <= m; i++)
    	{
    		for(int j = i; j <= m; j++)
    		{
    			b[i] = (b[i] + s[j][i] * a[j] % p) % p;
    		}
    	}
    	int w = 1;
    	for(int i = 0; i <= m; i++)
    	{
    		int tmp = ksm(x+1,n-i);
    		ans = (ans + b[i] * w % p * tmp % p * ksm(x,i) % p) % p;
    		w = w * (n-i) % p;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    DAY1T3 魔法商店

    论文题,不会写,咕咕咕。

    DAY2T1 信号传递

    题意描述

    洛谷

    给你一个长度为 (n) 的序列 (s), (s_i)(s_{i+1}) 之间有三种传递方式:

    • (s_{i+1})(s_{i}) 的右边,花费为 (p_{s_{i+1}}-p_{s_{i}})
    • (s_{i+1})(s_i) 的左边,花费为 (k imes (p_{s_{i+1}}+s_{i}))
    • (s_{i+1}=s_i) 则花费为 (0)

    其中 (p) 是一个大小为 (m) 的排列,(p_i) 表示 (i) 在排列 (p) 中的位置。

    现在让你确定一个排列 (P), 使得传递的花费最少。

    数据范围:(nleq 10^5,mleq 23)

    solution

    毒瘤卡常状压题。

    有一个很显然的状压 (dp) 的想法就是设 (f[s]) 表示已经确定了前 (|s|) 个位置上的数,且这些位置上的数的集合为 (s)

    其中 (s) 是一个 (0/1) 串,(|s|) 表示 (s)(1) 的个数,若 (s) 的第 (i) 位为 (1) 则表示第 (i+1) 个数的位置已经被确定了,为 (0) 则相反) 。

    (t[i][j]) 表示 (s_k)(i)(s_{k+1})(j) 的次数。这个在输入的时候就可以预处理出来。

    转移则有:(displaystyle f[s ext{^} (1<<(i-1))] = min(f[s] + (|s|+1) imes sum_{jin s} (k*t[i][j]+t[j][i])) + (|s|+1) imes sum_{j otin s} (k*t[j][i]+t[i][j]))

    解释一下:我们枚举第 (|s|+1) 位置上的数,设其为 (i), 那么 (jin s) 则表示 (j)(i) 的左边,反之 (j otin s)(j)(i) 的右边。分情况讨论一下传递的代价:

    • (j)(i) 的左边,传递方向为 (i ightarrow j), 则代价为 ((|s|+1) imes k imes t[i][j])
    • (j)(i) 的左边,传递方向为 (j ightarrow i), 则代价为 ((|s|+1) imes t[j][i])
    • (j)(i) 的右边,传递方向为 (i ightarrow j) ,则代价为 ((|s|+1) imes t[i][j])
    • (j)(i) 的右边,传递方向为 (j-i), 则代价为 ((|s|+1) imes k imes t[j][i])

    我们从小到大枚举 (s), 就可以做到 (O(m^22^m)) 的时间复杂度,空间复杂度则为 (O(m2^m))

    然后我做到这里就不太会了,看了看题解才过了这道题。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int N = 1<<23;
    int n,m,K,x,last;
    int f[N],t[24][24];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    int main()
    {
    	n = read(); m = read(); K = read();
    	for(int i = 1; i <= n; i++)
    	{
    		x = read();
    		if(i > 1) t[last][x]++;
    		last = x;
    	}
    	memset(f,127,sizeof(f));
    	f[0] = 0;
    	for(int i = 0; i < (1<<m); i++)
    	{
    		int cnt = 0;
    		for(int j = 1; j <= m; j++) if((i>>(j-1))&1) cnt++;
    		for(int j = 1; j <= m; j++)
    		{
    			if(!((i>>(j-1))&1))
    			{
    				int sum = 0;
    				for(int k = 1; k <= m; k++)
    				{
    					if((i>>(k-1))&1) sum += K*t[j][k], sum += t[k][j];  
    					else if(j != k) sum += K*t[k][j], sum -= t[j][k];
    				}		
    				f[i^(1<<(j-1))] = min(f[i^(1<<(j-1))],f[i]+sum*(cnt+1));
    			} 
    
    		}
    	}
    	printf("%d
    ",f[(1<<m)-1]);
    	return 0;
    }
    

    考虑继续卡常优化一下。

    (cost(s,i))(sum_{jin s} (k*t[i][j]+t[j][i])) + sum_{j otin s} (k*t[j][i]+t[i][j])) ,也就是 (|s|+1) 前的系数。

    处理完这个 (cost(s,i)) 那么我们在转移 (f) 的时候复杂度就降为了 (O(m2^m))

    预处理 (cost(s,i)) 其实是可以 (O(m2^m)) 的做的。具体来说就是:

    (displaystyle cost(0,i) = sum_{j eq i} k imes t[j][i] + t[i][j])

    (cost(s,i) = cost(s-(1<<(j-1),i) + t[i][j] imes (1+k) + t[j][i] imes (1-k)) (其中 (s) 的第 (j-1) 位为 (1))。

    解释一下: 对于 (cost(0,i)) 这时候 (j(j eq i)) 都在 (i) 的右边,所以传递代价为 (k imes t[j][i]+t[i][j])

    (s-(1<<(j-1)))(s) 相当于是 (j) 原来在 (i) 的右边,现在到了左边的位置,我们只需要把 (j)(i) 的右边的代价减去,在加上 (j)(i) 的左边的传递代价, 即 (t[i][j] imes (1+k) + t[j][i] imes (1-k)) ,然后我们就可以由 (cost(s-(1<<(j-1)),i)) 算出 (cost(s,i))

    因为每次都是由 (s) 减去一个数转移过来的,所以可以每次减去 (lowbit(s)) 转移。

    那么递推 (cost(s,i)) 的复杂度就变为了 (O(m2^m))

    写完交上去之后,你会发现 ( ext{MLE}) 了,毒瘤出题人卡了一手空间。

    至于如何卡过去,可以看这篇 题解

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int N = 1<<23;
    int n,m,K,x,last;
    int t[24][24],lg[N],num[N],f[N],cost[N/2][24];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    int lowbit(int x){return x & (-x);}
    int main()
    {
    	n = read(); m = read(); K = read();
    	for(int i = 1; i <= n; i++)
    	{
    		x = read()-1;
    		if(i > 1) t[last][x]++;
    		last = x;
    	}
    	lg[0] = -1; num[0] = 0;
    	for(int i = 1; i < (1<<m); i++)
    	{
    		lg[i] = lg[i>>1] + 1;
    		num[i] = num[i>>1] + (i&1);
    	}
    	for(int i = 0; i < m; i++)
    	{
    		for(int j = 0; j < m; j++)
    		{
    			if(i != j) cost[0][i] += K * t[j][i] - t[i][j]; 
    		}
    		for(int j = 1; j < (1<<(m-1)); j++)
    		{
    			int k = lowbit(j), o = lg[k];
    			if(o >= i) o++;
    			cost[j][i] = cost[j^k][i] + t[i][o] * (1+K) + t[o][i] * (1-K);
    		}
    	}
    	memset(f,127,sizeof(f));
    	f[0] = 0;
    	for(int i = 1; i < (1<<m); i++)
    	{
    		for(int j = i, y; y = lowbit(j); j ^= y)
    		{
    			int o = lg[y], s = i^y;
    			f[i] = min(f[i],f[i^y]+num[i]*cost[s&y-1|s>>o+1<<o][o]);
    		}
    	}
    	printf("%d
    ",f[(1<<m)-1]);
    	return 0;
    }
    

    DAY2T2 树

    题意描述

    洛谷

    给你一棵有根树,定义 (x) 节点的价值为 (x) 的子树中每个节点的点权加上到 (x) 的距离的异或和。

    即:(val(x)=(v_{c1}+d(c_1,x))⊕(v_{c2}+d(c_2,x))⊕⋯⊕(v_{c_k}+d(c_k,x)))

    让你求 (displaystylesum_{i=1}^{n} val(i))

    数据范围:(1leq n,v_ileq 525010)

    solution

    ( ext{tire}) 树/其他做法。

    对于这题,考虑怎么拿 (tire) 树来维护。

    我们对于每一个节点维护一个 ( ext{tire}) 树,( ext{tire}) 树里面存其子树中每个节点的点权加上到 (x) 的距离。

    暴力插数显然是过不去的,考虑继续优化。

    不难发现这样一个性质:对于 (u)( ext{tire}) 树中的每一个二进制数 (+1),恰好就是 (fa(u)) ( ext{tire}) 树中的数的一部分(这个可以根据 (val(u)) 的计算式推出来)。

    然后我们就要维护一下三种操作:

    • ( ext{tire}) 树中插入一个数。
    • (u)(fa(u))( ext{tire}) 树合并。
    • (u)( ext{tire}) 树中的二进制数 (+1)
    • 查询当前 ( ext{tire}) 树中所有数的异或和。

    操作1是 ( ext{tire}) 树的常规操作,这里就不说了。

    操作2的话,( ext{tire}) 树的合并其实和线段树合并是类似的,把 ( ext{tire}) 树看成一棵线段树,直接合并即可。

    这两个的不同点在于线段树的节点维护的是区间的信息,但 ( ext{tire}) 树的节点维护的每一位的信息。

    但合并的实质都是把两个节点维护的信息合并。

    void merage(int &x,int y,int wei)
    {
    	if(!x){x = y; return;}
    	if(!y) return;
    	siz[x] += siz[y];
    	merage(tr[x][0],tr[y][0],wei+1);
    	merage(tr[x][1],tr[y][1],wei+1);
    	up(x,wei);
    }
    

    对于操作3,我们考虑对于一个二进制数,加1之后会变成什么样,举几个例子:

    01111 -> 11111
    11011 -> 00111
    10011 -> 01011
    

    不难发现如果最低位为 (0) ,那么 (+1) 之后这一位就变为了 (1), 对于最低位为 (1) 的数,(+1) 之后这一位变为了 (0), 同时下一位进 (1) (相当于下一位+1)。

    ( ext{tire}) 树上,我们考虑从低位到高位建 ( ext{tire}) 树,+1操作就把当前节点的 ( ext{tr[x][0]})( ext{tire[x][1]}) 交换一下(+1之后,(0) 变为了 (1)(1) 变为了 (0)),在递归处理 ( ext{tr[x][0]}) (处理进位操作)。

    代码也很好写:

    void Jinwei(int x,int wei)//+1操作
    {
    	if(!x) return;
    	swap(tr[x][0],tr[x][1]);
    	Jinwei(tr[x][0],wei+1);
    	up(x,wei);
    }
    

    对于操作4,一开始我想的是维护每一位 (1) 的个数,但+1操作的时候需要遍历整颗 ( ext{tire}) 树,然后就挂掉了。

    但实际上我们只需要对 ( ext{tire}) 树的节点维护其子树中数的异或和即可(具体怎么维护可以看代码)。

    解决完这四个操作之后,我们这道题就做完了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    using namespace std;
    #define LL long long
    const int N = 550000;
    const int M = 5e7+10;
    int n,m,tot,cnt,x;
    int head[N],w[N],rt[N],siz[M],tr[M][2],sum[M];
    LL ans;
    struct node
    {
    	int to,net;
    }e[N];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    void add(int x,int y)
    {
    	e[++tot].to = y;
    	e[tot].net = head[x];
    	head[x] = tot;
    }
    void up(int o,int wei)
    {
        ////假设o维护的是第i位的信息,那么sum[o]表示o的子树中所有的数的只看 i~23 位,其他位忽略为0的异或和
    	sum[o] = sum[tr[o][0]] ^ sum[tr[o][1]];//首先sum[o] 一部分为其左儿子和右儿子的异或和,另一部分为这一位为1的数的贡献
    	if(siz[tr[o][1]]&1) sum[o] += 1<<wei;//如果这一位为1的数有奇数个那么异或起来这一位为1否则为0
    }
    void insert(int p,int x,int wei)//插入一个数
    {
    	if(wei > 23) return;
    	siz[p]++;
    	int c = (x>>wei)&1;
    	if(!tr[p][c]) tr[p][c] = ++cnt;
    	insert(tr[p][c],x,wei+1);
    	up(p,wei);
    }
    void merage(int &x,int y,int wei)//tire树合并
    {
    	if(!x){x = y; return;}
    	if(!y) return;
    	siz[x] += siz[y];
    	merage(tr[x][0],tr[y][0],wei+1);
    	merage(tr[x][1],tr[y][1],wei+1);
    	up(x,wei);
    }
    void Jinwei(int x,int wei)//+1操作
    {
    	if(!x) return;
    	swap(tr[x][0],tr[x][1]);
    	Jinwei(tr[x][0],wei+1);
    	up(x,wei);
    }
    void dfs(int x,int fa)
    {
    	for(int i = head[x]; i; i = e[i].net)
    	{
    		int to = e[i].to;
    		if(to == fa) continue;
    		dfs(to,x);
    		merage(rt[x],rt[to],0);
    	}
    	Jinwei(rt[x],0);
    	insert(x,w[x],0);
    	ans += sum[rt[x]];
    }
    int main()
    {
    	n = read(); cnt = n;
    	for(int i = 1; i <= n; i++) w[i] = read();
    	for(int i = 2; i <= n; i++) x = read(), add(x,i);
    	for(int i = 1; i <= n; i++) rt[i] = i; 
    	dfs(1,0);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    DAY2T3 作业题

    不会矩阵树。

  • 相关阅读:
    训练20191009 2018-2019 ACM-ICPC, Asia East Continent Finals
    [学习笔记] 有上下界网络流
    [HDU4609] 3-idiots
    [HDU1402] A * B Problem Plus
    [HNOI2017] 礼物
    训练20191007 2017-2018 ACM-ICPC Latin American Regional Programming Contest
    [ZJOI2014] 力
    训练20191005 2017-2018 ACM-ICPC Asia East Continent League Final
    [一本通学习笔记] 树链剖分
    [一本通学习笔记] 快速幂
  • 原文地址:https://www.cnblogs.com/genshy/p/14613054.html
Copyright © 2020-2023  润新知