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();}