A - STring
用类似括号匹配的方法搞一下即可…
#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int N=200005;
char s[N];int len,js=0,ans;
int main()
{
scanf("%s",s+1);
len=strlen(s+1);
for(RI i=1;i<=len;++i)
if(s[i]=='S') ++js;
else {
if(js) --js;
else ++ans;
}
printf("%d\n",ans+js);
return 0;
}
B - Minimum Sum
额,用单调栈找到每个元素左右第一个比它小的元素,就能知道左端点在哪个区间取右端点在哪个区间取,这个元素会是最小值,然后处理每个元素的贡献即可。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
typedef long long LL;
const int N=200005;
int n,top,L[N],R[N],a[N],st[N];LL ans;
int main()
{
n=read();
for(RI i=1;i<=n;++i) a[i]=read();
for(RI i=1;i<=n;++i) {
while(top&&a[st[top]]>a[i]) --top;
L[i]=(top?st[top]+1:1),st[++top]=i;
}
top=0;
for(RI i=n;i>=1;--i) {
while(top&&a[st[top]]>a[i]) --top;
R[i]=(top?st[top]-1:n),st[++top]=i;
}
for(RI i=1;i<=n;++i) ans+=1LL*(i-L[i]+1)*(R[i]-i+1)*a[i];
printf("%lld\n",ans);
return 0;
}
C - Tree Restoring
我们知道,树上一个点的最远点,一定是直径的两个端点之一。所以首先我们找出直径的长度 L L L。
如果 L L L是偶数,那么 a i = L 2 a_i=\frac{L}{2} ai=2L的点只能有一个, L 2 < a i ≤ L \frac{L}{2} <a_i \leq L 2L<ai≤L的点至少要有两个,如果有更多,则可以通过连接直径上 a j = a i − 1 a_j=a_i-1 aj=ai−1的点 j j j来达成目标。而 a i < L 2 a_i< \frac{L}{2} ai<2L的点不能存在。
如果 L L L是奇数,那么 a i = ⌈ L 2 ⌉ a_i=\lceil \frac{L}{2} \rceil ai=⌈2L⌉的点只能有两个, ⌈ L 2 ⌉ < a i ≤ L \lceil \frac{L}{2} \rceil <a_i \leq L ⌈2L⌉<ai≤L至少有两个, a i < ⌈ L 2 ⌉ a_i<\lceil \frac{L}{2} \rceil ai<⌈2L⌉不能存在。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int N=105;
int a[N],b[N],n,mx;
int main()
{
scanf("%d",&n);
for(RI i=1;i<=n;++i) scanf("%d",&a[i]),mx=max(mx,a[i]),++b[a[i]];
if(mx&1) {
for(RI i=mx;i>=(mx+1)/2;--i)
if(b[i]<2) {puts("Impossible");return 0;}
for(RI i=1;i<(mx+1)/2;++i)
if(b[i]) {puts("Impossible");return 0;}
if(b[(mx+1)/2]>2) {puts("Impossible");return 0;}
}
else {
for(RI i=mx;i>mx/2;--i)
if(b[i]<2) {puts("Impossible");return 0;}
for(RI i=1;i<mx/2;++i)
if(b[i]) {puts("Impossible");return 0;}
if(b[mx/2]!=1) {puts("Impossible");return 0;}
}
puts("Possible");
return 0;
}
D - ~K Perm Counting
考虑容斥,令 g i g_i gi表示钦定 i i i个点不合法,其他点随便的方案数。
答案就是 ∑ i = 0 n ( − 1 ) i + 1 g i ( n − i ) ! \sum_{i=0}^n (-1)^{i+1}g_i(n-i)! ∑i=0n(−1)i+1gi(n−i)!
那么怎么求 g i g_i gi呢?
假设有一张二分图,左边是每一个值,右边是每一个位子,每一个不合法的匹配,我们连一条边。我们发现这张图长得十分优美:
是若干条链。我们把所有的链都拉直了进行DP,设 f ( i , j , 0 / 1 ) f(i,j,0/1) f(i,j,0/1)表示前 i i i个点里选了 j j j个不合法匹配,且第 i i i个点和第 i + 1 i+1 i+1个点之间的匹配边是否选择的方案数,那么有:
f ( i + 1 , j , 0 ) = f ( i , j , 0 ) + f ( i , j , 1 ) , f ( i + 1 , j + 1 , 1 ) = f ( i , j , 0 ) f(i+1,j,0)=f(i,j,0)+f(i,j,1),f(i+1,j+1,1)=f(i,j,0) f(i+1,j,0)=f(i,j,0)+f(i,j,1),f(i+1,j+1,1)=f(i,j,0)
当然,如果一个点是某一条链的结尾,那么就要强制跳 f ( i + 1 , j + 1 , 1 ) = f ( i , j , 0 ) f(i+1,j+1,1)=f(i,j,0) f(i+1,j+1,1)=f(i,j,0)这个转移。
然后令 g ( i ) = f ( n , i , 0 ) + f ( n , i , 1 ) g(i)=f(n,i,0)+f(n,i,1) g(i)=f(n,i,0)+f(n,i,1)即可算出答案。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int mod=924844033,N=4005;
int vis[N][2],ed[N],f[N][N][2],fac[N],js,n,K,ans;
int qm(int x) {return x>=mod?x-mod:x;}
int main()
{
scanf("%d%d",&n,&K);
for(RI i=1;i<=n;++i)
for(RI j=0;j<=1;++j) {
if(vis[i][j]) continue;
for(RI x=i,y=j;x<=n;x+=K,y^=1) ++js,vis[x][y]=1;
ed[js]=1;
}
f[1][0][0]=1;
for(RI i=1;i<=js;++i)
for(RI j=0;j<=n;++j) {
f[i+1][j][0]=qm(f[i][j][0]+f[i][j][1]);
if(!ed[i]) f[i+1][j+1][1]=f[i][j][0];
}
fac[0]=1;for(RI i=1;i<=n;++i) fac[i]=1LL*fac[i-1]*i%mod;
for(RI i=0,fh=1;i<=n;++i,fh=-fh)
ans=qm(ans+qm(1LL*fh*qm(f[js][i][0]+f[js][i][1])%mod*fac[n-i]%mod+mod));
printf("%d\n",ans);
return 0;
}
E - Sugigma: The Showdown
若有红树上有一条边,它的两个端点在蓝树上的距离大于2,则如果先手到达了这两个端点,就必胜。
建出蓝树,以后手起点为根,算出所有点深度。然后建出红树,删掉满足以上条件的边(也就是建的时候就不加)
然后看先手能到那些点。
由于红树上任意一条边连接的两点在蓝树上的距离小于等于2,所以如果一个点到先手起点的距离大于等于到后手起点的距离,先手走这点就一定会被后手抓,所以不能走。这样如果它能到必胜点,就输出-1,否则选择在蓝树上深度最大的那个点,待在那里坐以待毙。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
const int N=200005;
int n,s1,s2,tim,ans;
int win[N],dep[N],in[N],out[N],fa[N],vis[N],dis[N],que[N];
struct edge{int x,y;}e1[N];
struct tree{
int h[N],ne[N<<1],to[N<<1],tot;
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
}T1,T2;
void dfs(int x,int las) {
dep[x]=dep[las]+1,in[x]=++tim,fa[x]=las;
for(RI i=T2.h[x];i;i=T2.ne[i])
if(T2.to[i]!=las) dfs(T2.to[i],x);
out[x]=tim;
}
int check(int x,int y) {
if(in[x]>in[y]) swap(x,y);
if(in[x]<=in[y]&&out[x]>=in[y]) return dep[y]-dep[x]>2;
if(fa[x]==fa[y]) return 0;
return 1;
}
void bfs() {
int he=1,ta=1;
vis[s1]=1,dis[s1]=0,que[1]=s1;
while(he<=ta) {
int x=que[he];++he;
for(RI i=T1.h[x];i;i=T1.ne[i])
if(dis[x]+1<dep[T1.to[i]]&&!vis[T1.to[i]])
vis[T1.to[i]]=1,dis[T1.to[i]]=dis[x]+1,que[++ta]=T1.to[i];
}
}
int main()
{
int x,y;
n=read(),s1=read(),s2=read();
if(s1==s2) {puts("0");return 0;}
for(RI i=1;i<n;++i) e1[i].x=read(),e1[i].y=read();
for(RI i=1;i<n;++i) x=read(),y=read(),T2.add(x,y),T2.add(y,x);
dep[0]=-1,dfs(s2,0);
for(RI i=1;i<n;++i) {
if(check(e1[i].x,e1[i].y)) win[e1[i].x]=win[e1[i].y]=1;
else T1.add(e1[i].x,e1[i].y),T1.add(e1[i].y,e1[i].x);
}
bfs();
for(RI i=1;i<=n;++i)
if(win[i]&&vis[i]) {puts("-1");return 0;}
else if(vis[i]) ans=max(ans,dep[i]+dep[i]);
printf("%d\n",ans);
return 0;
}
F - Many Easy Problems
容斥。
考虑每一个点对答案的贡献,发现如果一个选点集合的f中,x这个点没有参与贡献,则所有集合中的点都在删掉x的那些连通块中,也就是说,x对k意义下的答案的贡献为:
C
n
k
−
∑
C
a
i
k
C_n^k-\sum C_{a_i}^k
Cnk−∑Caik
其中 a i a_i ai为删掉点 x x x的那些连通块的大小。
我们发现对于任意一个 k k k,一个指定的 C i k C_i^k Cik的系数是不变的,预处理这个系数,设为 p i p_i pi。
则:
a n s k = ∑ i = k n C i k p i = 1 k ! ∑ i = k n p i i ! ( i − k ) ! ans_k=\sum_{i=k}^n C_i^kp_i=\frac{1}{k!}\sum_{i=k}^n \frac{p_ii!}{(i-k)!} ansk=i=k∑nCikpi=k!1i=k∑n(i−k)!pii!
我们发现,构造一个 a i = p i i ! a_i=p_ii! ai=pii!和一个 b n − i = 1 i ! b_{n-i}=\frac{1}{i!} bn−i=i!1,则 a a a与 b b b的卷积的第 i + k i+k i+k项就是 k k k意义下的答案。
题目给出的模数的原根是5,可以NTT。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
const int N=200005,LIM=524288,mod=924844033,G=5;
int n,tot;
int h[N],ne[N<<1],to[N<<1],sz[N],p[LIM],tmp[LIM],rev[LIM],fac[N],ni[N];
int qm(int x) {return x>=mod?x-mod:x;}
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
void pre_dfs(int x,int las) {
sz[x]=1;p[n]=qm(p[n]+1);
for(RI i=h[x];i;i=ne[i]) {
if(to[i]==las) continue;
pre_dfs(to[i],x),sz[x]+=sz[to[i]];
p[sz[to[i]]]=qm(p[sz[to[i]]]-1+mod);
}
if(n-sz[x]) p[n-sz[x]]=qm(p[n-sz[x]]-1+mod);
}
int ksm(int x,int y) {
int re=1;
for(;y;y>>=1,x=1LL*x*x%mod) if(y&1) re=1LL*re*x%mod;
return re;
}
void NTT(int *a,int n,int x) {
for(RI i=0;i<n;++i) if(rev[i]>i) swap(a[i],a[rev[i]]);
for(RI i=1;i<n;i<<=1) {
int gn=ksm(G,(mod-1)/(i<<1));
for(RI j=0;j<n;j+=(i<<1)) {
int t1,t2,g=1;
for(RI k=0;k<i;++k,g=1LL*g*gn%mod) {
t1=a[j+k],t2=1LL*g*a[j+i+k]%mod;
a[j+k]=qm(t1+t2),a[j+i+k]=qm(t1-t2+mod);
}
}
}
if(x==1) return;
int inv=ksm(n,mod-2);reverse(a+1,a+n);
for(RI i=0;i<n;++i) a[i]=1LL*a[i]*inv%mod;
}
int main()
{
int x,y,kn=1,len=0;
n=read();
for(RI i=1;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
pre_dfs(1,0);fac[0]=1;
for(RI i=1;i<=n;++i)
fac[i]=1LL*fac[i-1]*i%mod,p[i]=1LL*p[i]*fac[i]%mod;
ni[n]=ksm(fac[n],mod-2);
for(RI i=n-1;i>=0;--i) ni[i]=1LL*(i+1)*ni[i+1]%mod;
for(RI i=0;i<=n;++i) tmp[n-i]=ni[i];
while(kn<=n+n) kn<<=1,++len;
for(RI i=1;i<kn;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
NTT(p,kn,1),NTT(tmp,kn,1);
for(RI i=0;i<kn;++i) tmp[i]=1LL*tmp[i]*p[i]%mod;
NTT(tmp,kn,-1);
for(RI i=1;i<=n;++i) printf("%lld\n",1LL*tmp[n+i]*ni[i]%mod);
return 0;
}