首页 > 其他分享 >Codeforces Round #848 (Div. 2) A~F 题解

Codeforces Round #848 (Div. 2) A~F 题解

时间:2023-02-02 11:44:05浏览次数:30  
标签:tmp ve int 题解 848 xxj Div include MOD

A. Flip Flop Sum

能换 \(-1,-1\) 就换,不能能换 \(1,-1\) 或 \(-1,1\) 也可以,否则只能换 \(1,1\)。

B. The Forbidden Permutation

如果原序列一开始就是好的那就结束,否则尝试打破一边的不等号即可。

C. Flexible String

暴力搜索每一个字符是否在 \(S\) 当中。时间复杂度 \(O(n2^{|\Sigma|})\)

D. Flexible String Revisit

随机给 \(01\) 串取反,问变到全 \(0\) 的期望步数。

考虑 \(f_i\) 表示当前有 \(i\) 个 \(1\) 的期望步数。不难有:

\[f_0=0,f_i=\dfrac inf_{i-1}+\dfrac{n-i}n f_{i+1}+1 \]

我们假设 \(f_n=x\),我们可以把每一个 \(f\) 写成关于 \(x\) 的一次函数,推回到 \(f_0\) 时解出 \(x\) 即可。

给一段参考代码。

void init(){
	// 求 p[n]
	a[n]=1,b[n]=0;
	for(int i=n;i>=1;i--){
		// ip[i-1]=np[i]-(n-i)p[i+1]-n
		a[i-1]=(n*a[i]%MOD-(n-i)*a[i+1]%MOD+MOD)%MOD*inv(i)%MOD;
		b[i-1]=((n*b[i]%MOD-(n-i)*b[i+1]%MOD+MOD)%MOD-n+MOD)*inv(i)%MOD;
	} 
	ll x=(MOD-b[0])*inv(a[0])%MOD;
	for(int i=0;i<=n;i++)
		p[i]=(a[i]*x+b[i])%MOD;
}

E. The Tree Has Fallen!

  • 给一棵点权树,每次询问查询以 \(r\) 为根时 \(u\) 的子树当中可选若干点权异或最大。
  • \(n,q\leq2\times 10^5\)。

首先这个问题显然时线性基,我们考虑对每个结点维护一个线性基存入他的子树点权。由于根可能被改变,所以需要换根。

更具体的,当我们把根从 \(u\) 换到 \(v\) 的时候,那么 \(v\) 的线性基就会变成全部元素的线性基,但是 \(u\) 的线性基会变成所有节点去除 \(v\) 节点的线性基的合并。但是线性基没有可减性,所以我们需要维护一个节点的相邻节点的前缀线性基,后缀线性基才可以完成转移。

代码写起来很丑,而且常熟巨大,当乐子看就好了。听说有 dfs 序的简单一点的代码,但是懒得打。

#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define ll long long
#define MP make_pair 
const int MAXN=2e5+5;
int _;
struct Xxj{
	int a[32];
}xxj[MAXN];
int ask(Xxj x){
	ll res=0;
	for(int i=31;i>=0;i--)
		if((res&(1<<i))==0)
			res^=x.a[i];
	return res;
}
void insert(Xxj &x,int k){
	for(int i=31;i>=0;i--)
		if(!x.a[i]&&(k&(1<<i))){
			x.a[i]=k;
			return;
		}else if(k&(1<<i))k^=x.a[i];
}
Xxj merge(Xxj x,Xxj y){
	Xxj res=x;
	for(int i=31;i>=0;i--)
		if(y.a[i])
			insert(res,y.a[i]);
	return res;
}
void Clear(Xxj &x){
	for(int i=31;i>=0;i--) x.a[i]=0;
}
vector<int> ve[MAXN];
vector<pair<int,int> > qe[MAXN];
int ans[MAXN];
int n,q,a[MAXN];
void dfs(int u,int fa){
	insert(xxj[u],a[u]);
	for(int v:ve[u]) if(v!=fa){
		dfs(v,u);
		xxj[u]=merge(xxj[u],xxj[v]);
	}
} 
void dfs_root(int u,int fa){
	for(auto i:qe[u])
		ans[i.second]=ask(xxj[i.first]);
	int sz=ve[u].size();
	vector<Xxj> pl(sz+1),pr(sz+1); 
	pl[0]=xxj[ve[u][0]];
	pr[sz-1]=xxj[ve[u][sz-1]];Clear(pr[sz]);
	for(int i=1;i<sz;i++)
		pl[i]=merge(pl[i-1],xxj[ve[u][i]]);
	for(int i=sz-2;i>=0;i--)
		pr[i]=merge(pr[i+1],xxj[ve[u][i]]);
	Xxj rt=xxj[u],copy;
	for(int i=0;i<sz;i++)if(ve[u][i]!=fa){
		if(!i) xxj[u]=pr[i+1];
		else xxj[u]=merge(pl[i-1],pr[i+1]);
		insert(xxj[u],a[u]);
		copy=xxj[ve[u][i]],xxj[ve[u][i]]=rt;
		dfs_root(ve[u][i],u);
		xxj[ve[u][i]]=copy;
	}
	xxj[u]=rt;
	return;
}
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++) ve[i].clear(),qe[i].clear(),Clear(xxj[i]);
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++){
		int u,v;cin>>u>>v;
		ve[u].push_back(v);
		ve[v].push_back(u);
	}
	cin>>q;
	for(int i=1;i<=q;i++){
		int r,u;cin>>r>>u;
		qe[r].push_back(MP(u,i));
	}
	dfs(1,0);
	dfs_root(1,0);
	for(int i=1;i<=q;i++)
		cout<<ans[i]<<endl;
	return;
}
int main(){
	cin>>_;
	while(_--)
		solve();
}

F. Maximizing Root

  • 给一颗点权树(\(1\) 为根),每次可以选择一个未选择过的节点 \(u\) 然后把他的所有儿子点权乘上这颗子树所有节点点权的 \(\gcd\)。
  • 最多 \(k\) 次操作,最大化 \(1\) 的点权。
  • \(n\leq10^5,a_i\leq10^3\)。

显然操作肯定时自下而上的,也就是不会选择了一个祖先又选择了一个儿子。对于一次操作来说,变化的是一个子树的 \(\gcd\) 变成了它本身的平方。

考虑树形 dp。\(f_{i,j}\) 表示对 \(i\) 的子树,不操作根节点要所有权值都是 \(j\) 的倍数的最小操作数,容易得到转移:

\[f_{i,j}=\sum_{v\in son_i} \min(f_{v,j},(\min_{j|k^2} f_{v,k})+1) \]

这个转移是 \(d(V)^2\) 的。总的时间复杂度时 \(O(n d^2(V))\),得以通过。

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
const int MAXN=1e5+5;
const int MR=1005;
int f[MAXN][1005];// f[i][j] 表示 i 子树公约数 j 倍数最小操作数 
vector<int> ve[MAXN],d[MR];
int tmp[1005],a[MAXN];
int n,k;
int gcd(int x,int y){
	return (x%y==0)?y:gcd(y,x%y);
}
void dfs(int u,int fa){
	for(int i:d[a[u]]) f[u][i]=0;
	for(int v:ve[u])if(v!=fa){
		dfs(v,u);
		for(int p:d[a[v]]) if(f[v][p]!=1e9){
			int g=gcd(a[u],p);
			tmp[g]=min(tmp[g],f[v][p]);
			g=gcd(a[u],p*p);
			tmp[g]=min(tmp[g],f[v][p]+1);
		}
		for(int i:d[a[u]])
			for(int j:d[i]) tmp[j]=min(tmp[j],tmp[i]);
		for(int i:d[a[u]])
			f[u][i]=min(f[u][i]+tmp[i],(int)1e9),tmp[i]=1e9;
	}
	return;
}
void solve(){
	cin>>n>>k;
	for(int i=1;i<=n;i++) ve[i].clear();
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++){
		int u,v;cin>>u>>v;
		ve[u].push_back(v);
		ve[v].push_back(u);
	}
	if(k==0){
		cout<<a[1]<<endl;
		return;
	}
	dfs(1,0);
	int ans=0;
	for(int i:d[a[1]])
		if(f[1][i]<k) ans=max(ans,a[1]*i);
	cout<<ans<<endl;
	return;
}
int main(){
	for(int i=1;i<MR;i++){
		tmp[i]=1e9;
		for(int j=i;j<MR;j+=i)
			d[j].push_back(i);
	}
	int _;cin>>_;
	while(_--) solve();
}

标签:tmp,ve,int,题解,848,xxj,Div,include,MOD
From: https://www.cnblogs.com/KawaiiOU/p/17085525.html

相关文章