T1按位或 T2最短路径 T3仙人掌 T4对弈


被踩了。想得不深,于是暴毙了。

T1 按位或

先不管 \(3\) 的限制,考虑如何求或出 \(t\) 的方案,很自然地想到可以容斥去做,枚举或出 \(t\) 中的位数即可。

加入 \(3\) 的限制后,深入观察可得,每个二进制位单独产生的数在模 \(3\) 意义下只会是 \(1\) 或 \(2\) ,且只要这两种位个数相等,它或出 \(3\) 的倍数的方案总是相等的。于是同样枚举或出的位数,枚举模出 \(1\) 和 \(2\) 的二进制位数即可。

求方案可以做背包。

\(code:\)

T1​

#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL 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;
}
void write(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=75,mod=998244353;
int n,t,ans,ext,re1,re2,bit,f[2][3];
namespace Combination{
int fac[NN],inv[NN];
int C(int x,int y){ return fac[x]*inv[y]%mod*inv[x-y]%mod; }
int qpow(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=res*a%mod;
a=a*a%mod;
}
return res;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=70;i++) fac[i]=fac[i-1]*i%mod;
inv[70]=qpow(fac[70],mod-2);
for(int i=69;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
} using namespace Combination;
int calc(int num1,int num2){
memset(f,0,sizeof(f));
int typ=1; f[1][0]=1;
for(int i=1;i<=num1;i++){
int now=typ^1;
memcpy(f[now],f[typ],sizeof(f[typ]));
for(int j=0;j<3;j++) (f[now][(j+1)%3]+=f[typ][j])%=mod;
typ=now;
}
for(int i=1;i<=num2;i++){
int now=typ^1;
memcpy(f[now],f[typ],sizeof(f[typ]));
for(int j=0;j<3;j++) (f[now][(j+2)%3]+=f[typ][j])%=mod;
typ=now;
}
return f[typ][0];
}
signed main(){
freopen("or.in","r",stdin);
freopen("or.out","w",stdout);
n=read(); t=read(); init();
for(int i=0;(1ll<<i)<=t;i++)
if(t&(1ll<<i)) (1ll<<i)%3==1?++re1:++re2;
bit=re1+re2;
for(int i=0,typ=1;i<=bit;i++,typ=-typ){
int lmt=min(re1,i);
for(int j=0;j<=lmt;j++){
if(i-j>re2) continue;
(ans+=typ*qpow(calc(re1-j,re2-i+j),n%(mod-1))*C(re1,j)%mod*C(re2,i-j))%=mod;
}
}
write((ans+mod)%mod,'\n');
return 0;
}


T2 最短路径

稍加思考可得最短路径即虚树上边数总和的二倍减去两点间最长的路径长度。

边数总和的期望较好求,一条边被计入答案的概率即它两边都有饼干的概率。

对于最长路径,为了不重不漏,枚举每一条路径 \(dis(u,v)\) ,规定它是虚树中端点字典序最小的直径。那么对于另外一点 \(w\) ,它不能存在于虚树中,当且仅当满足下面几个条件中至少一种:


  • \(dis(u,v)<dis(u,w)\)
  • \(dis(u,v)<dis(v,w)\)
  • \(dis(u,v)=dis(v,w) \wedge w<u\)
  • \(dis(u,v)=dis(u,w) \wedge w<v\)

求出合法点数,概率即剩下 \(k-2\) 个饼干分布在这些点中的概率。

\(code:\)

T2​

#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL 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;
}
void write(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=2010,mod=998244353;
int n,m,k,tot,res,base;
bool clue[NN];
vector<int>cl;
namespace Combination{
int fac[NN],inv[NN];
int C(int x,int y){ return x<0||y<0||x<y?0:fac[x]*inv[y]%mod*inv[x-y]%mod; }
int qpow(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=res*a%mod;
a=a*a%mod;
}
return res;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
} using namespace Combination;
namespace graph{
struct edge{ int to,nex; }e[NN<<1];
int idx,id[NN<<1],head[NN];
int siz[NN],dis[NN][NN];
void add(int a,int b,int c){
e[++idx]=(edge){b,head[a]}; head[a]=idx;
e[++idx]=(edge){a,head[b]}; head[b]=idx;
}
void dfs(int s,int f){
siz[s]=clue[s];
for(int i=head[s],v=e[i].to;i;i=e[i].nex,v=e[i].to) if(v!=f){
dfs(v,s);
(base+=mod+1-(C(siz[v],k)+C(m-siz[v],k))*tot%mod)%=mod;
siz[s]+=siz[v];
}
}
void getdis(int s,int f,int anc,int d){
if(clue[s]) dis[anc][s]=d;
for(int i=head[s],v=e[i].to;i;i=e[i].nex,v=e[i].to)
if(v!=f) getdis(v,s,anc,d+1);
}
} using namespace graph;
void prprprpr(){
init(); tot=qpow(C(m,k),mod-2);
dfs(1,0);
for(int i:cl) getdis(i,0,i,0);
base=base*2%mod;
}
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read(); m=read(); k=read();
for(int a,i=1;i<=m;i++)
clue[a=read()]=1,cl.push_back(a);
for(int a,b,i=1;i<n;i++)
a=read(),b=read(),add(a,b,i);
prprprpr();
for(int i:cl)
for(int j:cl) if(i<j){
int cnt=0;
for(int k:cl) if((k^i)&&(k^j)){
if(dis[i][k]>dis[i][j]) continue;
if(dis[j][k]>dis[i][j]) continue;
if(dis[i][j]==dis[i][k]&&k<j) continue;
if(dis[i][j]==dis[j][k]&&k<i) continue;
++cnt;
}
(res+=C(cnt,k-2)*tot%mod*dis[i][j])%=mod;
}
write((base+mod-res)%mod,'\n');
return 0;
}


T3 仙人掌

暂留

T4 对弈

2021.11.2考试总结[冲刺NOIP模拟21]_#include

2021.11.2考试总结[冲刺NOIP模拟21]_考试_02

数位DP,最后石子数总和为 \(w\) ,则总方案为

\[f_{20,w}\times \binom{n-\frac{k}{2}-w}{\frac{k}{2}}\]

后面组合数,考虑石子之外的空隙有 \(n-k-w\) ,将它有顺序分为 \(k+1\) 个非负整数,插板法。

\(code:\)

T4​

#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL 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;
}
void write(LL x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;
const int NN=10010,mod=1e9+7;
int n,k,m,ans,res,f[21][NN];
namespace Combination{
int fac[NN],inv[NN];
int C(int x,int y){ return fac[x]*inv[y]%mod*inv[x-y]%mod; }
int qpow(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=res*a%mod;
a=a*a%mod;
}
return res;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
} using namespace Combination;
int dfs(int pos,int num){
if(f[pos][num]>=0) return f[pos][num];
if(!pos&&!num) return f[pos][num]=1;
if(!pos) return f[pos][num]=0;
f[pos][num]=0;
for(int i=0;i<=k;i+=m+1){
int tmp=(1<<pos-1)*i;
if(tmp>num) continue;
(f[pos][num]+=dfs(pos-1,num-tmp)*C(k,i))%=mod;
}
return f[pos][num];
}
signed main(){
// freopen("chess.in","r",stdin);
// freopen("chess.out","w",stdout);
n=read(); k=read()/2; m=read(); init();
memset(f,-1,sizeof(f));
ans=C(n,k<<1);
for(int i=n-2*k;~i;i--)
(res+=dfs(20,i)*C(n-k-i,k))%=mod;
ans=(mod+ans-res)%mod;
write(ans,'\n');
return 0;
}