2021.10.11考试总结[NOIP模拟74]

T1 自然数

发现\(mex\)是单调不降的,很自然地想到用线段树维护区间端点的贡献。

枚举左端点,用线段树维护每个右端点形成区间的\(mex\)值。每次左端点右移相当于删去一个数。

\(a_i\)\(i\)下一次出现的位置为\(pos_i\),那么左端点\(i\)移到\(i+1\),实际上就是将左端点在\([i,pos_i)\)的区间中\(mex\)值大于\(a_i\)的改为\(a_i\)。线段树上二分可以解决,中途要记区间最小值便于二分。

\(code:\)

T1
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	typedef long long LL;
	auto read=[]()->int{
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	};
	auto write=[](LL x,int sp)->void{
		char ch[20]; int len=0;
		if(x<0){ x=~x+1; putchar('-'); }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	};
	auto ckmax=[](int& x,int y)->void{ x=x<y?y:x; };
	auto ckmin=[](int& x,int y)->void{ x=x<y?x:y; };
} using namespace IO;

const int NN=200010;
int n,ans,tmp,a[NN];
vector<int>loc[NN];
bool bin[NN];

namespace segment_tree{
	#define ld rt<<1
	#define rd (rt<<1)|1
	int sum[NN<<2],mnn[NN<<2],tag[NN<<2],len[NN<<2];
	void pushup(int rt){
		sum[rt]=sum[ld]+sum[rd];
		mnn[rt]=min(mnn[ld],mnn[rd]);
	}
	void pushdown(int rt){
		if(tag[rt]<0) return;
		sum[ld]=tag[rt]*len[ld];
		sum[rd]=tag[rt]*len[rd];
		mnn[ld]=mnn[rd]=tag[rt];
		tag[ld]=tag[rd]=tag[rt];
		tag[rt]=-1;
	}
	void build(int rt,int l,int r){
		len[rt]=r-l+1; tag[rt]=-1;
		if(l==r) return;
		int mid=l+r>>1;
		build(ld,l,mid);
		build(rd,mid+1,r);
	}
	void insert(int rt,int l,int r,int pos,int val){
		if(l==r) return sum[rt]=mnn[rt]=val,void();
		int mid=l+r>>1;
		if(pos<=mid) insert(ld,l,mid,pos,val);
		else insert(rd,mid+1,r,pos,val);
		pushup(rt);
	}
	void modify(int rt,int l,int r,int opl,int opr,int val){
		if(l==r){
			if(mnn[rt]>val) mnn[rt]=sum[rt]=val;
			return;
		}
		pushdown(rt);
		int mid=l+r>>1;
		if(l>=opl&&r<=opr){
			if(mnn[rt]>=val){
				mnn[rt]=tag[rt]=val;
				sum[rt]=val*len[rt];
			} else if(mnn[rd]>=val){
				mnn[rd]=tag[rd]=val;
				sum[rd]=val*len[rd];
				modify(ld,l,mid,opl,opr,val);
				pushup(rt);
			} else modify(rd,mid+1,r,opl,opr,val),pushup(rt);
			return;
		}
		if(opl<=mid) modify(ld,l,mid,opl,opr,val);
		if(opr>mid) modify(rd,mid+1,r,opl,opr,val);
		pushup(rt);
	}
	int query(int rt,int l,int r,int opl,int opr){
		if(l>=opl&&r<=opr) return sum[rt];
		pushdown(rt);
		int mid=l+r>>1,res=0;
		if(opl<=mid) res+=query(ld,l,mid,opl,opr);
		if(opr>mid) res+=query(rd,mid+1,r,opl,opr);
		return res;
	}
} using namespace segment_tree;

signed main(){
	freopen("mex.in","r",stdin);
	freopen("mex.out","w",stdout);
	n=read(); build(1,1,n);
	for(int i=1;i<=n;i++){
		a[i]=read();
		if(a[i]<=n)
			loc[a[i]].push_back(i), bin[a[i]]=1;
		while(bin[tmp]) ++tmp;
		insert(1,1,n,i,tmp);
	}
	for(int i=0;i<=n;i++)
		if(loc[i].size())
			loc[i].push_back(n+1);
	for(int i=1;i<=n;i++){
		ans+=query(1,1,n,i,n);
		if(a[i]>n) continue;
		int pos=*upper_bound(loc[a[i]].begin(),loc[a[i]].end(),i);
		modify(1,1,n,i,pos-1,a[i]);
	}
	return write(ans,'\n'),0;
}

T2 钱仓

对于正整数\(a,b\)\(a^2+b^2<(a+b)^2\)。于是有贪心:断环为链后从后往前扫,每次遇到有东西的仓库,就把东西移到最靠后的位置。

发现不管怎么贪心,最后都是最优解。于是找到一个合法端点贪心一遍即可。

\(code:\)

T2
#include<bits/stdc++.h>
#define int long long 
using namespace std;

namespace IO{
	auto read=[]()->int{
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	};
	auto write=[](int x,int sp)->void{
		char ch[20]; int len=0;
		if(x<0){ x=~x+1; putchar('-'); }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	};
	auto ckmax=[](int& x,int y)->void{ x=x<y?y:x; };
	auto ckmin=[](int& x,int y)->void{ x=x<y?x:y; };
} using namespace IO;

const int NN=100010;
int n,s,mx,pre,res,c[NN<<1],o[NN];
int calc(int x){ return x*(x+1)*(2*x+1)/6; }

signed main(){
	freopen("barn.in","r",stdin);
	freopen("barn.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) c[i]=c[i+n]=read();
	for(int i=1;i<=n;i++){
		pre+=c[i]-1;
		if(pre<0) s=i+1,pre=0;
	} pre=0;
	for(int i=s+n-1;i>=s;i--){
		if(!c[i]){ if(!pre) pre=i; }
		else if(pre) res+=calc(pre-i)-calc(pre-i-c[i]),pre-=c[i];
	}
	return write(res,'\n'),0;
}
/*
10
1
0
0
2
0
0
1
2
2
2

*/

T3 游戏

\(Alice\)\(A\)\(Bob\)\(B\),设\(a_n\)\(b_n\)分别为\(A\)\(B\)在剩\(n\)个石子时先手,\(A\)的胜率。

一些结论:

  • 若A先手,A认为现在想投某一面更优,那么在当前这个石子被某一个人拿走之前她会一直想投这一面,B先手时对于B也是如此
  • 若A先手,为了阻止A投到某一面,B也要选择投这一面,也就是说对于任意一个石子,A和B的决策相同

不难推出转移式子:

\(a_n\geq b_n\):

\[a_{n+1}=(1-p)\times b_n+p\times(1-q)\times a_n+p\times q\times a_{n+1} \]

\[a_{n+1}=\frac{p\times(1-q)\times a_n+(1-p)\times b_n}{1-p\times q} \]

同理,

\[b_{n+1}=(1-q)\times a_n+q\times(1-p)\times b_n+p\times q\times b_{n+1} \]

\[b_{n+1}=\frac{(1-q)\times a_n+q\times(1-p)\times b_n}{q-p\times q} \]

\(a_n\leq b_n\)时类似,

\[a_{n+1}=\frac{q\times(1-p)\times a_n+p\times b_n}{p+q-p\times q} \]

\[b_{n+1}=\frac{q\times a_n+p\times(1-q)\times b_n}{p+q-p\times q} \]

image

实际上就是\(n\)为奇数时用第一种转移,为偶数时用第二种转移。将奇偶转移合为一次转移,矩阵优化。

\(code:\)

T3
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	auto read=[]()->int{
		char ch=getchar(); int x=0,f=1;
		while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
		while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	};
	auto write=[](int x,int sp)->void{
		char ch[20]; int len=0;
		if(x<0){ x=~x+1; putchar('-'); }
		do{ ch[len++]=(1<<4)+(1<<5)+x%10; x/=10; }while(x);
		for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp);
	};
	auto ckmax=[](int& x,int y)->void{ x=x<y?y:x; };
	auto ckmin=[](int& x,int y)->void{ x=x<y?x:y; };
} using namespace IO;

const int mod=1e9+7;
int T,n,p,q;
int qpow(int a,int b){
	int res=1; a%=mod;
	for(;b;b>>=1){
		if(b&1) res=res*a%mod;
		a=a*a%mod;
	}
	return res;
}
const int inv=qpow(100000000,mod-2),inv3=qpow(3,mod-2);

namespace Matrix{
	struct mat{
		int s[3][3];
		mat(){}
		mat(int x){ memset(s,0,sizeof(s)); s[1][1]=s[2][2]=x; }
		mat operator*(const mat& a)const{
			mat res=mat(0);
			for(int i=1;i<3;i++)
				for(int k=1;k<3;k++)
					for(int j=1;j<3;j++)
						(res.s[i][j]+=s[i][k]*a.s[k][j])%=mod;
			return res;
		}
	}s,t,t1,t2;
	mat operator^(mat a,int b){
		mat res=mat(1);
		for(;b;b>>=1){
			if(b&1) res=res*a;
			a=a*a;
		}
		return res;
	}
	void prework(){
		s.s[1][2]=1; s.s[1][1]=0;
		t1.s[1][1]=(mod+1-p)*q%mod*qpow(mod+p+q-p*q%mod,mod-2)%mod;
		t1.s[2][1]=p*qpow(mod+p+q-p*q%mod,mod-2)%mod;
		t1.s[1][2]=q*qpow(mod+p+q-p*q%mod,mod-2)%mod;
		t1.s[2][2]=(mod+1-q)*p%mod*qpow(mod+p+q-p*q%mod,mod-2)%mod;
		t2.s[1][1]=p*(mod+1-q)%mod*qpow(mod+1-p*q%mod,mod-2)%mod;
		t2.s[2][1]=(mod+1-p)*qpow(mod+1-p*q%mod,mod-2)%mod;
		t2.s[1][2]=(mod+1-q)*qpow(mod+1-p*q%mod,mod-2)%mod;
		t2.s[2][2]=q*(mod+1-p)%mod*qpow(mod+1-p*q%mod,mod-2)%mod;
		t=t1*t2;
	}
} using namespace Matrix;

signed main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	T=read();
	while(T--){
		n=read(); p=read(); q=read();
		p=p*inv%mod; q=q*inv%mod;
		prework();
		t=t^(n/2); s=s*t;
		if(n&1) s=s*t1;
		write(s.s[1][1],'\n');
	}
	return 0;
}
posted @ 2021-10-11 19:08  keen_z  阅读(70)  评论(0编辑  收藏  举报