AtCoder Grand Contest 005题解
\(A\)
咕咕
const int N=5e5+5;
char s[N];int res,n,sum;
int main(){
scanf("%s",s+1),res=n=strlen(s+1);
fp(i,1,n)if(s[i]=='S')++sum;
else if(sum)--sum,res-=2;
printf("%d\n",res);
return 0;
}
\(B\)
咕咕
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=2e5+5,inf=0x3f3f3f3f;
int a[N],l[N],r[N],st[N],n,top;ll res;
int main(){
scanf("%d",&n);
fp(i,1,n)scanf("%d",&a[i]);
a[0]=a[n+1]=0,st[++top]=0;
fp(i,1,n){
while(top&&a[i]<a[st[top]])--top;
l[i]=st[top],st[++top]=i;
}
st[top=1]=n+1;
fd(i,n,1){
while(top&&a[i]<a[st[top]])--top;
r[i]=st[top],st[++top]=i;
}
fp(i,1,n)res+=1ll*a[i]*(i-l[i])*(r[i]-i);
printf("%lld\n",res);
return 0;
}
\(C\)
因为树上所有直径有同一个中点或同一条中边,那么\(dis\)最小的点只能有\(1\)个或者两个
然后我们从中点开始构造一条直径,如果无法构造出无解,否则剩下的点都可以挂在直径上而满足条件
最后判一下构造出的直径是否满足它给出的\(dis\)即可
const int N=105;
int c[N],n,mn=233,mx;
int main(){
scanf("%d",&n);
for(R int i=1,x;i<=n;++i)scanf("%d",&x),++c[x],cmin(mn,x),cmax(mx,x);
if(c[mn]>2)return puts("Impossible"),0;
fp(i,mn+1,mx)if(c[i]<2)return puts("Impossible"),0;
if(c[mn]==2&&mn!=mx-mn+1)return puts("Impossible"),0;
if(c[mn]==1&&mn!=mx-mn)return puts("Impossible"),0;
return puts("Possible"),0;
}
\(D\)
这种题目显然是容斥了
然而如果直接枚举有几个点选了不该选的数会有问题,因为有的点选不该选的数有两种方案,有的点选了别的点就不能选
发现选了之后\(i\)只会影响\(...,i-4k,i-2k,i+2k,i+4k,...\)的数的选法,那么我们把所有模\(2k\)意义下相等的位置一起考虑,记\(f_{i,j,0/1/2}\)表示考虑到这一类中的第\(i\)个数,选了其中\(j\)个,第\(i\)个数不选\(/\)选择在这个位置放\(i-k/\)选择在这个位置放\(i+k\)的方案数,转移即可,最后暴力加到全局的背包里,即可计算出\(s[i]\)表示至少有这\(i\)个位置选择不合法的方案数,就可以直接套容斥了
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
const int P=924844033;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
R int res=1;
for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
return res;
}
const int N=2005;
int n,k,res,len,f[N][3],g[N][3],s[N],t[N],fac[N];
void calc(R int x){
memset(f,0,sizeof(f));
f[0][0]=1;
R int p=0;
for(;x<=n;x+=(k<<1)){
fp(i,0,p){
g[i][0]=f[i][0],g[i][1]=f[i][1],g[i][2]=f[i][2];
f[i][0]=f[i][1]=f[i][2]=0;
}
fp(i,0,p){
R int s=add(g[i][0],add(g[i][1],g[i][2]));
upd(f[i][0],s);
if(x>k)upd(f[i+1][1],dec(s,g[i][2]));
if(x+k<=n)upd(f[i+1][2],s);
}
++p;
}
memset(t,0,sizeof(t));
fp(i,0,len)fp(j,0,p)upd(t[i+j],mul(s[i],add(f[j][0],add(f[j][1],f[j][2]))));
len+=p;
fp(i,0,len)s[i]=t[i];
}
int main(){
// freopen("testdata.in","r",stdin);
scanf("%d%d",&n,&k);
fac[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
s[0]=1;
fp(i,1,min(k<<1,n))calc(i);
fp(i,0,len)upd(res,i&1?dec(0,mul(fac[n-i],s[i])):mul(fac[n-i],s[i]));
printf("%d\n",res);
return 0;
}
\(E\)
博弈论菜鸡在此
首先如果存在一个情况,\(A\)在\(u\),\(B\)在\(v\),如果\(u\)能走到的任何一条边在蓝树上到\(v\)的距离都小于等于\(1\),\(A\)只能等死了,否则如果存在一条边\((u,v)\)满足蓝树上的距离大于\(2\),那么\(A\)就可以一直利用这条边绕\(B\)了,此时输出\(-1\)
如果会被追到,那么以蓝树上的\(Y\)为根,我们发现\(A\)根本走不出这棵子树,那么我们找到所有\(A\)能到达的点,然后到其中深度最大的点等死就行了
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(head,u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
const int N=1e6+5;
struct eg{int v,nx;}e[N<<1];int bl[N],rd[N],tot;
inline void add(R int *head,R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int fr[N],to[N],dep[N],fa[N],dfn[N],low[N],ok[N],vis[N],dis[N],tim,n,S,T;
void dfs(int *head,int u){
dfn[u]=++tim;
go(head,u)if(v!=fa[u])fa[v]=u,dep[v]=dep[u]+1,dfs(head,v);
low[u]=tim;
}
inline bool ck(R int u,R int v){
if(dfn[u]>dfn[v])swap(u,v);if(low[u]>=low[v])return dep[v]-dep[u]>2;
if(fa[u]==fa[v])return false;return true;
}
int q[N];
void bfs(){
int h=1,t=0,u;q[++t]=S,vis[S]=1;
while(h<=t){
u=q[h++];
go(rd,u)if(!vis[v]){
dis[v]=dis[u]+1;
if(dis[v]<dep[v])vis[v]=1,q[++t]=v;
}
}
}
int main(){
scanf("%d%d%d",&n,&S,&T);
for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),fr[i]=u,to[i]=v;
for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),add(bl,u,v),add(bl,v,u);
dfs(bl,T);
for(R int i=1,u,v;i<n;++i){
u=fr[i],v=to[i];
if(ck(u,v))ok[u]=ok[v]=1;
else add(rd,u,v),add(rd,v,u);
}
bfs();
fp(i,1,n)if(vis[i]&&ok[i])return puts("-1"),0;
R int res=0;
fp(i,1,n)if(vis[i])cmax(res,dep[i]<<1);
printf("%d\n",res);
return 0;
}
\(F\)
先考虑如何计算单个\(k\),那么对于每个点分别计算贡献,一个点会被计入贡献的次数是\({n\choose k}-\sum_{v\in son_u}{sz_v\choose k}\),其中\(sz_v\)表示以\(u\)为根时\(v\)子树的大小
那么\(n\)个点加起来就是\(n\times {n\choose k}-\sum_{i=1}^n cnt[i]\times {i\choose k}\),把组合数拆成阶乘的形式就可以把后面看成一个卷积了,\(NTT\)即可
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=924844033,gi=554906420;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
R int res=1;
for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
return res;
}
const int N=(1<<19)+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void Add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int fac[N],ifac[N],r[25][N],rt[2][N],f[N],g[N],inv[25];
int n,lim,d;
inline void swap(R int &x,R int &y){R int t=x;x=y,y=t;}
inline int C(R int n,R int m){return m>n?0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
void init(){
fac[0]=ifac[0]=1;fp(i,1,262144)fac[i]=mul(fac[i-1],i);
ifac[262144]=ksm(fac[262144],P-2);fd(i,262143,1)ifac[i]=mul(ifac[i+1],i+1);
fp(d,1,19){
fp(i,1,(1<<d)-1)r[d][i]=(r[d][i>>1]>>1)|((i&1)<<(d-1));
inv[d]=ksm(1<<d,P-2);
}
for(R int t=(P-1)>>1,i=1,x,y;i<=262144;i<<=1,t>>=1){
x=ksm(5,t),y=ksm(gi,t),rt[0][i]=rt[1][i]=1;
fp(k,1,i-1)
rt[1][i+k]=mul(rt[1][i+k-1],x),
rt[0][i+k]=mul(rt[0][i+k-1],y);
}
}
void NTT(int *A,int ty){
fp(i,0,lim-1)if(i<r[d][i])swap(A[i],A[r[d][i]]);
R int t;
for(R int mid=1;mid<lim;mid<<=1)
for(R int j=0;j<lim;j+=(mid<<1))
fp(k,0,mid-1)
A[j+k+mid]=dec(A[j+k],t=mul(rt[ty][mid+k],A[j+k+mid])),
A[j+k]=add(A[j+k],t);
if(!ty){
t=inv[d];
fp(i,0,lim-1)A[i]=mul(A[i],t);
}
}
int sz[N];
void dfs(int u,int fa){
sz[u]=1;
go(u)if(v!=fa){
dfs(v,u),sz[u]+=sz[v];
++f[n-sz[v]],++f[sz[v]];
}
}
int main(){
// freopen("testdata.in","r",stdin);
scanf("%d",&n);
for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),Add(u,v),Add(v,u);
dfs(1,0);init();
lim=1,d=0;
while(lim<=(n<<1))lim<<=1,++d;
fp(i,0,n)f[i]=mul(f[i],fac[i]),g[i]=ifac[n-i];
NTT(f,1),NTT(g,1);
fp(i,0,lim-1)f[i]=mul(f[i],g[i]);
NTT(f,0);
fp(i,1,n)printf("%d\n",dec(mul(n,C(n,i)),mul(f[i+n],ifac[i])));
return 0;
}