Noip模拟88 2021.11.2


T1 按位或

考场上刚了两个多小时的,自认为\(T1\)位运算疯狂找规律就能切的\(sb\)神仙题

然而是容斥+dp。。。。。。

话说考场上写想过容斥,当时是容斥有几位和\(t\)不同,但是不会\(dp\)试图\(O(1)\)算出方案但是不会(逃)

不扯了说正解

我们发现\(2^i \mod 3\)只会取到\(1,2\)两种值,那么我们可以把\(t\)二进制拆分后对所有的\(1\)分成两类

设模三余一的位有\(num1\)个,余二的有\(num2\)个,总共\(m\)\(1\)

然后容斥枚举\(t\)有几位上的\(1\)\(0\),设\(S_k\)表示有\(k\)原本是\(1\)而不是\(1\)的方案数

那么答案就是\(\sum\limits_{k=0}^{m}(-1)^kS_k\)

考虑求\(S_k\),使用背包

\(dp_{i,j,0/1/2}\)表示选了\(i\)\(\mod 3=1\)的位,选了\(j\)\(\mod 3=2\)的位,
然后生成的数\(x\mod 3\)\(0/1/2\)的方案数

\(dp[i][j][(u+1)\mod 3]+=dp[i-1][j][u]\)

\(dp[i][j][(u+2)\mod 3]+=dp[i][j-1][u]\)

分这两种情况转移就行

那么每次转移完以后\(dp[i][j][0]\)就是可以使用的容斥的方案数,他的贡献可以累加到\(S_{m-i-j}\)

贡献就是\(C_{num1}^{i}\times C_{num2}^{j}\times (dp[i][j][0])^n\)

后面的乘方就可以理解为每一个数都可以选择这么多种方案,因为他们都是\(3\)的倍数

这样就求出了\(S_k\)

or
#include
#define int long long
using namespace std;
const int NN=65,mod=998244353;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		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,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;
int n,t,ans,m,pw[NN],cnt[3];
namespace Math{
	auto qmo=[](int a,int b,int ans=1){
		int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
		return ans;
	};
	int h[NN],v[NN];
	auto prework=[](){
		h[0]=h[1]=1; v[0]=v[1]=1;
		for(int i=2;i=2;i--)v[i]=v[i+1]*(i+1)%mod;
		pw[0]=1;for(int i=1;i<=63;i++)pw[i]=pw[i-1]*2;
		for(int i=0;i<=63;i++) if(t&pw[i]){++cnt[pw[i]%3];++m;}
	};
	auto C=[](int n,int m){return (n

T2 最短路径

考试的时候搞错最短路径的求法了,于是只有\(20pts\)

考虑如果以选出的\(k\)个点建立“虚树”(是假的别害怕),那么他们之间的最短距离是边数总和\(\times 2-\)虚树直径

比较好理解(但是考场上就是想不出来,太菜)

所求的期望就可以转化为求虚树边长之和的期望虚树直径的期望
求边长之和的期望只要枚举每条边,它出现在虚树的概率就是这条边两边都有小饼干的概率

补充两句求法就是记录一个\(num[x]\)表示以\(x\)为根的子树中关键点的个数

那么枚举边\((u,v)\),记录他的两边的关键点分别为\(a,b\)个,保证\(dep[u]>dep[v]\)

这样\(a=num[u],b=m-num[u]\),这条边的期望值就是\(\frac{C_{m}^{k}-C_{a}^{k}-C_{b}^{k}}{C_{m}^{k}}\)

求虚树直径的期望可以暴力一点
钦定一棵树中的某个点对\((u,v)\)为直径,当且仅当\(dis(u,v)\)最长且字典序最小,并且钦定\(u,那么直径是唯一的
枚举所有点对\((u,v)(u,考虑这条路径是直径的概率
首先,考虑点对\((u,w)(w!=v)\)\((w,v)(w!=u)\)的影响
即先保证不会出现其他以\(u\)\(v\)的端点的路径为直径
那么我们发现,对于一个点 ,它不能出现当且仅当下面几个条件之一被满足
\(dis(u,w)>dis(u,v)\)
\(dis(u,w)=dis(u,v) \And w
\(dis(v,w)>dis(u,v)\)
\(dis(v,w)=dis(u,v) \And w
我们数出能出现的点数\(cnt\),算概率就是算其他\(k-2\)个小饼干都落在这\(cnt\)个里面的概率

确实很清楚,直接放代码了

tree
#include
#define int long long
using namespace std;
const int NN=2005,mod=998244353,MM=305;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		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,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;
namespace Math{
	auto qmo=[](int a,int b,int ans=1){
		int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
		return ans;
	};
	int h[NN],v[NN];
	auto prework=[](){
		h[0]=h[1]=1; v[0]=v[1]=1;
		for(int i=2;i=2;i--)v[i]=v[i+1]*(i+1)%mod;
	};
	auto C=[](int n,int m){return (ndfn[y]) swap(x,y);
	return x;
};
int dis[NN][NN];

namespace WSN{
	inline short main(){
		FILE *wsn=freopen("tree.in","r",stdin);wsn=freopen("tree.out","w",stdout);
		n=read(); m=read(); k=read(); prework();
		for(int i=1;i<=m;i++) id[i]=read(),po[id[i]]=1;
		for(int i=1,u,v;idis[i][j]||dis[k][j]>dis[i][j])continue;
				if(dis[i][k]==dis[i][j]&&k

T3 仙人掌

太难了线性代数+仙人掌上dp,咕咕咕

T4 对弈

原题链接

可以进行双倍经验

证明很详细,结论比较淦,仔细看就好

\(\color{white}{其实是拍的。。。。}\)

chess
#include
#define int long long
using namespace std;
const int NN=1e4+5,mod=1e9+7;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		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,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;
int n,k,m,dp[21][NN],ans;
namespace Math{
	int h[NN],v[NN];
	auto qmo=[](int a,int b,int ans=1){
		int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
		return ans;
	};
	auto prework=[](){
		h[0]=h[1]=1; v[0]=v[1]=1;
		for(int i=2;i=2;i--)v[i]=v[i+1]*(i+1)%mod;
	};
	auto C=[](int n,int m){return (nnum) continue;
		(dp[pos][num]+=dfs(pos-1,num-tmp)*C(k,i)%mod)%=mod;
	}
	return dp[pos][num];
}
namespace WSN{
	inline short main(){
		FILE *wsn=freopen("chess.in","r",stdin);
		wsn=freopen("chess.out","w",stdout);
		n=read();k=read()/2;m=read();prework();
		ans=C(n,k*2);memset(dp,-1,sizeof(dp));
		for(int i=n-2*k;~i;i--)
			ans=(ans-dfs(20,i)*C(n-i-k,k)%mod+mod)%mod;
		write(ans);
		return 0;
	}
}
signed main(){return WSN::main();}