190328省选模拟

取石子

这题分 a a a是否等于 1 1 1分类讨论一下,将 [ a , b ] [a,b] [a,b]设成不可达状态来手玩 s g sg sg函数值
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
int n,a,b;
int main(){
	for(ri tt=read();tt;--tt){
		n=read(),a=read(),b=read();
		bool f=0;
		int sum=0;
		for(ri x,sg,i=1;i<=n;++i){
			x=read();
			if(f||x<a)continue;
			if(x>=a&&x<=b){f=1;continue;}
			else if(a==1)sg=(x-(b+1))%(b+1);
			else{
				x=(x-b)%(a+b)/a;
				if(x<2)sg=x^1;
				else sg=x;
			}
			sum^=sg;
		}
		if(f){puts("Alice");continue;}
		puts(sum?"Alice":"Bob");
	}
	return 0;
}

序列

d p dp dp神题。
O r z Orz Orz题解+找高二神仙学姐讨论了一波,大概能看懂 s t d std std在写啥。
定义几个数组:
f i , j f_{i,j} fi,j表示长度限制为 i i i,最终填完之后第一个是 j j j的概率。
g i , j g_{i,j} gi,j表示长度限制为 i i i,最终填完之后第一个是 j j j的序列和的期望。
p i , j p_{i,j} pi,j表示长度限制为 i i i,最后弄出来只有第一个为 j j j之后的格子都为空的概率。
E i , j E_{i,j} Ei,j表示初始一波操作过后第一个放的是 j j j,最终填完之后序列和的期望。(注意到填完之后第一个就不一定是 j j j了)。
然后维护一个数组 s s s,其中 s i s_i si表示长度为 i i i的序列的答案来优化转移。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int mod=1e9+7,N=2005;
int f[N][N],g[N][N],p[N][N],E[N][N],s[N],n,m,t,invm;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
int main(){
	scanf("%d%d%d",&n,&m,&t);
	invm=ksm(m,mod-2);
	for(ri i=1,up;i<=n;++i){
		up=min(t,i+m-1);
		for(ri j=1,coe;j<=up;++j){
			coe=dec(1,j==t?0:p[i-1][j]);
			p[i][j]=add(mul(p[i][j-1],p[i-1][j-1]),j<=m?invm:0);
			f[i][j]=mul(p[i][j],coe);
			g[i][j]=j==t?add(j,s[i-1]):add(j,mul(dec(s[i-1],mul(p[i-1][j],E[i-1][j])),ksm(coe,mod-2)));
			s[i]=add(s[i],mul(f[i][j],g[i][j]));
		}
		for(ri j=up;j;--j)E[i][j]=add(mul(p[i-1][j],E[i][j+1]),mul(dec(1,j==t?0:p[i-1][j]),g[i][j]));
	}
	cout<<s[n];
	return 0;
}

这个就直接上容斥+并查集就完了吧,注意由于是多次询问需要写一个按秩合并的可删除并查集。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+5,M=1e6+5;
int anc[N],n,m,mul[M],top=0,siz[N],mu[M],sig=0,mp[N],pri[M],tot=0,val[N];
bool chk[M],vis[M],ban[N],is[M];
struct edge{int u,v,w;}e[N];
struct Node{int id,w;}upd[N];
vector<int>fac[M],coe[M],id[M];
pii stk1[N],stk2[N];
ll ans[N],sum=0;
inline int find(const int&x){return x^anc[x]?find(anc[x]):x;}
inline int get(int x){
	if(mul[x])return mul[x];
	if(x<2)return mul[x]=x;
	int ret=1;
	for(ri i=0;i<fac[x].size();++i)ret*=fac[x][i];
	return mul[x]=ret;
}
inline void get_coef(int pos,int mul,int a){
	if(pos==fac[a].size()){coe[a].push_back(mul);return;}
	get_coef(pos+1,mul,a),get_coef(pos+1,fac[a][pos]*mul,a);
}
inline void calc(int x){
	if(chk[x])return;
	chk[x]=1,get_coef(0,1,x);
}
inline void init(){
	for(ri i=2;i<=1000000;++i)if(!vis[i])for(ri j=i;j<=1000000;j+=i)vis[j]=1,fac[j].push_back(i);
	for(ri i=1;i<=n;++i)anc[i]=i,siz[i]=1;
	for(ri fx,fy,i=1;i<n;++i){
		e[i].w=get(e[i].w),calc(e[i].w);
		if(ban[i])for(ri j=0;j<coe[e[i].w].size();++j)is[coe[e[i].w][j]]=1;
		else for(ri j=0;j<coe[e[i].w].size();++j)id[coe[e[i].w][j]].push_back(i);
	}
	for(ri i=1;i<=m;++i){
		upd[i].w=get(upd[i].w),calc(upd[i].w);
		for(ri j=0;j<coe[upd[i].w].size();++j)is[coe[upd[i].w][j]]=1;
	}
	mu[1]=1;
	memset(vis,0,sizeof(vis));
	for(ri i=2;i<=1000000;++i){
		if(!vis[i])pri[++tot]=i,mu[i]=-1;
		for(ri j=1;j<=tot&&i*pri[j]<=1000000;++j){
			vis[i*pri[j]]=1;
			if(i==i/pri[j]*pri[j]){mu[i*pri[j]]=0;break;}
			mu[i*pri[j]]=-mu[i];
		}
	}
}
inline void merge(int fx,int fy){
	fx=find(fx),fy=find(fy);
	if(siz[fx]>siz[fy])swap(fx,fy);
	++top;
	stk1[top]=pii(fx,anc[fx]),anc[fx]=fy;
	sum+=(ll)siz[fx]*siz[fy];
	stk2[top]=pii(fy,siz[fy]),siz[fy]+=siz[fx];
}
inline void solve(){
	for(ri tt=1;tt<=1000000;++tt){
		if(!mu[tt])continue;
		for(ri qq=0,o;qq<id[tt].size();++qq)o=id[tt][qq],merge(e[o].u,e[o].v);
		int tim=top;
		ll ps=sum;
		for(ri i=0;i<=m;++i){
			if(is[tt]){
				for(ri j=1;j<=sig;++j)val[mp[j]]=e[mp[j]].w;
				for(ri j=1;j<=i;++j)val[upd[j].id]=upd[j].w;
				for(ri j=1;j<=sig;++j)if(!(val[mp[j]]%tt))merge(e[mp[j]].u,e[mp[j]].v);
			}
			ans[i]+=sum*mu[tt];
			while(tim^top)anc[stk1[top].fi]=stk1[top].se,siz[stk2[top].fi]=stk2[top].se,--top;	
			sum=ps;
		}
		while(top)anc[stk1[top].fi]=stk1[top].se,siz[stk2[top].fi]=stk2[top].se,--top;
		sum=0;
	}
	for(ri i=0;i<=m;++i)cout<<ans[i]<<'\n';
}
signed main(){
	n=read();
	for(ri i=1;i<n;++i)e[i].u=read(),e[i].v=read(),e[i].w=read();
	m=read();
	for(ri i=1;i<=m;++i)ban[upd[i].id=mp[++sig]=read()]=1,upd[i].w=read();
	sort(mp+1,mp+sig+1),sig=unique(mp+1,mp+sig+1)-mp-1;
	init();
	solve();
	return 0;
}

还有一种更快的写法:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+5,M=1e6+5,T=205;
int lim=0,n,m,mp[T],sig=0,top=0,anc[N],siz[N],val[N],mu[M],pri[M];
bool vis[M],ban[N],is[M];
struct edge{int u,v,w;}e[N];
struct Node{int id,w;}upd[T];
vector<int>id[M];
ll ans[T],sum=0;
pii s1[N],s2[N];
inline int find(const int&x){return x^anc[x]?find(anc[x]):x;}
inline void merge(int x,int y){
	x=find(x),y=find(y);
	if(siz[x]>siz[y])swap(x,y); 
	++top;
	s1[top]=pii(x,anc[x]),anc[x]=y;
	sum+=(ll)siz[x]*siz[y];
	s2[top]=pii(y,siz[y]),siz[y]+=siz[x];
}
inline void init(){
	for(ri i=1;i<=n;++i)anc[i]=i,siz[i]=1;
	mu[1]=1;
	for(ri i=2,tot=0;i<=lim;++i){
		if(!vis[i])mu[i]=-1,pri[++tot]=i;
		for(ri j=1;j<=tot&&i*pri[j]<=lim;++j){
			vis[i*pri[j]]=1;
			if(i==i/pri[j]*pri[j]){mu[i*pri[j]]=0;break;}
			mu[i*pri[j]]=-mu[i];
		}
	}
	for(ri i=1;i<n;++i){
		for(ri j=1;j*j<=e[i].w;++j){
			if(e[i].w%j)continue;
			if(mu[j]){
				if(ban[i])is[j]=1;
				else id[j].push_back(i);
			}
			if(j*j!=e[i].w&&mu[e[i].w/j]){
				if(ban[i])is[e[i].w/j]=1;
				else id[e[i].w/j].push_back(i);
			}
		}
	}
	for(ri i=1;i<=m;++i)for(ri j=1;j*j<=upd[i].w;++j)if(!(upd[i].w%j))is[j]=is[upd[i].w/j]=1;
}
inline void solve(){
	for(ri tt=1;tt<=lim;++tt){
		if(!mu[tt])continue;
		for(ri i=0;i<id[tt].size();++i)merge(e[id[tt][i]].u,e[id[tt][i]].v);
		int tim=top;
		ll pre=sum;
		for(ri i=0;i<=m;++i){
			if(is[tt]){
				for(ri j=1;j<=sig;++j)val[mp[j]]=e[mp[j]].w;
				for(ri j=1;j<=i;++j)val[upd[j].id]=upd[j].w;
				for(ri j=1;j<=sig;++j)if(!(val[mp[j]]%tt))merge(e[mp[j]].u,e[mp[j]].v);
			}
			ans[i]+=sum*mu[tt];
			sum=pre;
			while(top^tim)anc[s1[top].fi]=s1[top].se,siz[s2[top].fi]=s2[top].se,--top;
		}
		while(top)anc[s1[top].fi]=s1[top].se,siz[s2[top].fi]=s2[top].se,--top;
		sum=0;
	}
}
int main(){
	n=read();
	for(ri i=1;i<n;++i)e[i].u=read(),e[i].v=read(),lim=max(lim,e[i].w=read());
	m=read();
	for(ri i=1;i<=m;++i)ban[upd[i].id=mp[++sig]=read()]=1,lim=max(lim,upd[i].w=read());
	sort(mp+1,mp+sig+1),sig=unique(mp+1,mp+sig+1)-mp-1;
	init();
	solve();
	for(ri i=0;i<=m;++i)cout<<ans[i]<<'\n';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值