【学习笔记】ARC156

文章讲述了在解决C-Tree问题时采用叶子节点两两配对的策略,以及在处理LCS路径问题中遇到的思考过程。同时,文章探讨了D-XorSum问题,利用生成函数和背包算法来计算所有S的异或和,通过优化降低复杂度到O(nA_ilogK)。
摘要由CSDN通过智能技术生成

C - Tree and LCS

服了,又不会构造题了。。。

答案是 1 1 1

最初的想法是构造一个小根堆和一个大根堆,后来想了一下感觉没法处理路径的情况。

然后看了一下题解,哦,把叶子节点两两配对就行了,看起来很对的样子。其实也就相当于 p p p序列是把这条路径翻转过来了。那么问题来了,为什么我当时就没想到呢?

数据范围你也诈骗人呢?

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
int n,in[5005],p[5005];
vector<int>g[5005];
queue<int>Q;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);	
	cin>>n;
	for(int i=1;i<n;i++){
		int x,y;cin>>x>>y,g[x].pb(y),g[y].pb(x),in[x]++,in[y]++;
	}for(int i=1;i<=n;i++)if(in[i]==1)Q.push(i);
	int x=0;
	while(Q.size()){
		int y=Q.front();Q.pop();
		if(x)p[x]=y,p[y]=x,x=0;
		else x=y;
		for(auto z:g[y]){
			if(--in[z]==1)Q.push(z);
		}
	}if(x)p[x]=x;
	for(int i=1;i<=n;i++)cout<<p[i]<<' ';
}

D - Xor Sum 5

考虑用生成函数来解决这个问题。

相当于求所有 S S S的异或和,其中 [ x S ] ( x A 1 + x A 2 + . . . + x A n ) K [x^S](x^{A_1}+x^{A_2}+...+x^{A_n})^K [xS](xA1+xA2+...+xAn)K的值为 1 1 1(在模 2 2 2意义下)

注意到 ( x A 1 + x A 2 + . . . + x A n ) 2 k = x A 1 2 k + x A 2 2 k + . . . + x A n 2 k (x^{A_1}+x^{A_2}+...+x^{A_n})^{2^k}=x^{A_12^k}+x^{A_22^k}+...+x^{A_n2^k} (xA1+xA2+...+xAn)2k=xA12k+xA22k+...+xAn2k

K = ∑ i = 1 M 2 k i K=\sum_{i=1}^M2^{k_i} K=i=1M2ki,那么原多项式可以写成:

[ x S ] ∏ i = 1 M ( x A 1 2 k i + x A 2 2 k i + . . . + x A n 2 k i ) [x^S]\prod_{i=1}^M(x^{A_12^{k_i}}+x^{A_22^{k_i}}+...+x^{A_n2^{k_i}}) [xS]i=1M(xA12ki+xA22ki+...+xAn2ki)

这样相当于算 ∑ i = 1 M A X i 2 k i \sum_{i=1}^MA_{X_i}2^{k_i} i=1MAXi2ki的异或和, K K K的范围就大大缩小了。

最无脑的想法就是直接背包。然而应该注意到,其体积的形式为 A X i 2 k i A_{X_i}2^{k_i} AXi2ki,也就是说在 A i A_i Ai的二进制表示下在末尾添 k i k_i ki个零,显然后 k i k_i ki位的值不会发生变化,因此我们可以提前将后 k i k_i ki位的答案确定出来,稍加分析可知背包容量只需要 2 max ⁡ ( A i ) 2\max(A_i) 2max(Ai),总复杂度 O ( n A i log ⁡ K ) O(nA_i\log K) O(nAilogK)

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
int n,m,A,a[2005],p[2005],dp[2005],dp2[2005],ans1[2005],ans2[2005];
ll K,tot;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);	
	cin>>n>>K;for(int i=1;i<=n;i++)cin>>a[i],A=max(A,a[i]);
	int b=0;
	while(K){
		if(K&1)p[++m]=b;
		K>>=1,b++;
	}dp[0]=1;
	for(int i=1;i<=m;i++){
		memset(dp2,0,sizeof dp2),memset(ans1,0,sizeof ans1);
		ll mo=1ll<<p[i]-p[i-1];
		for(int j=0;j<=2*A;j++){
			if(dp[j]){
				for(int k=1;k<=n;k++){	
					dp2[a[k]+j/mo]^=1;
					ans1[j%mo]^=1;
				}
			}
		}if(n&1){
			ll res=0;
			for(int j=0;j<=2*A;j++){
				if(ans1[j]){
					res^=j;
				}
			}tot+=res<<p[i-1];
		}
		if(i==m){
			ll res=0;
			for(int j=0;j<=2*A;j++){
				if(dp2[j]){
					res^=j;
				}
			}tot+=res<<p[i];
			cout<<tot;
		}memcpy(dp,dp2,sizeof dp2);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值