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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)