Description
Solution
首先,不难发现,无论先手与后手如何操作,后手总是赢的。也就是说,题目所要求的答案,就是存在多少种极大格局。
直接考虑操作格局较为困难,于是考虑最终的盘面格局。不难发现:
①由于它需要是极大的,所以:
-
不存在连续两个空格。
-
对于某个空格而言,其两侧必然是一个 A \text{A} A,一个 B \text{B} B。
②由于它是合法的,所以:
-
不存在两个连续的 A \text{A} A。
-
不存在两个连续的 B \text{B} B。
通过手玩可以发现,若将所有空格全部去掉,那么最终剩下的必然是 ABAB ⋯ AB \text{ABAB}\cdots\text{AB} ABAB⋯AB 或 BABA ⋯ BA \text{BABA}\cdots \text{BA} BABA⋯BA。
由于存在两种不同的串(开始为 A A A 或开始为 B B B),所以需要乘 2 2 2。当其串长为 s s s 时,那么一共走了 s s s 步,每步都可以随意在某个位置上放置,所要还要乘上 s ! s! s!。同时,每一对相邻的 A,B \text{A,B} A,B 间和两侧最多只能插入一个空格,且最靠左的字母左边与最靠有的字母右边不能同时插入空格,还要乘上这一部分的方案数。于是分类讨论:
-
若最左侧( 1 1 1 号位置)为空格,那么最右侧不能是空格,所以只能在 s − 1 s-1 s−1 个位置中插入剩下的 n − s − 1 n-s-1 n−s−1 个空格,贡献为 ( s − 1 n − s − 1 ) {s-1 \choose n-s-1} (n−s−1s−1)。
-
若最左侧( n n n 号位置)不为空格,那么最右侧可以是空格,方案数为 ( s n − s ) {s \choose n-s} (n−ss)。
得到答案:
∑ 2 ∣ i 2 i ! ( ( i − 1 n − i − 1 ) + ( i n − i ) ) \sum_{2|i} 2i!\left({i-1 \choose n-i-1}+{i \choose n-i}\right) 2∣i∑2i!((n−i−1i−1)+(n−ii))
阶乘逆元预处理,然后 O ( n ) O(n) O(n) 计算即可。 总复杂度 O ( n ) O(n) O(n)。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxl=5005,maxg=20,mod=998244353;
int read(){
int s=0,w=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') w=-w;ch=getchar();}
while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
return s*w;
}
int n,m,tot,cnt;
int head[maxl],lim[maxl],root[maxl],fa[maxl][maxg],leav[maxl],dep[maxl],lg[maxl];
struct edge{int nxt,to;}e[maxl<<1];
struct Segment_tree{
int lson,rson,val;
int tag_plus,tag_assign;
}tree[maxl*maxg];
void add_edge(int u,int v){
cnt++;
e[cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt;
}
void dfs(int now,int fath){
dep[now]=dep[fath]+1,fa[now][0]=fath;
for (int i=1;i<=lg[dep[now]];i++) fa[now][i]=fa[fa[now][i-1]][i-1];
leav[now]=1;
for (int i=head[now];i;i=e[i].nxt){
int y=e[i].to;
if (y==fath) continue;
dfs(y,now),leav[now]=0;
}
}
int up_tree(int x,int y){
for (int i=0;i<lg[dep[x]];i++){
if (y&(1<<i)) x=fa[x][i];
}
return x;
}
void pushup(int rt){
tree[rt].val=(tree[tree[rt].lson].val+tree[tree[rt].rson].val)%mod;
}
void f_assign(int l,int r,int rt,int k){
if (k==1) tree[rt].val=1,tree[rt].tag_assign=1;
else tree[rt].val=-1,tree[rt].tag_assign=-1;
}
void f_add(int l,int r,int rt,int k){
tree[rt].val+=k*(r-l+1);
tree[rt].tag_plus+=k;
}
void pushdown(int l,int r,int rt){
int mid=(l+r)>>1,ls=tree[rt].lson,rs=tree[rt].rson;
if (tree[rt].tag_assign){
int x=tree[rt].tag_assign;
if (x==-1) x++;
f_assign(l,mid,ls,x);
f_assign(mid+1,r,rs,x);
}
if (tree[rt].tag_plus){
int x=tree[rt].tag_plus;
f_assign(l,mid,ls,x);
f_assign(mid+1,r,rs,x);
}
tree[rt].tag_assign=tree[rt].tag_plus=0;
}
int change_assign(int nl,int nr,int l,int r,int rt,int k){
if (!rt) rt=++tot;
else pushdown(l,r,rt);
if (nl<=l&&r<=nr){
f_assign(l,r,rt,k);
return rt;
}
int mid=(l+r)>>1;
if (nl<=mid) tree[rt].lson=change_assign(nl,nr,l,mid,tree[rt].lson,k);
else tree[rt].rson=change_assign(nl,nr,mid+1,r,tree[rt].rson,k);
pushup(rt);
return rt;
}
int change_add(int nl,int nr,int l,int r,int rt,int k){
if (!rt) rt=++tot;
else pushdown(l,r,rt);
if (nl<=l&&r<=nr){
f_add(l,r,rt,k);
return rt;
}
int mid=(l+r)>>1;
if (nl<=mid) tree[rt].lson=change_add(nl,nr,l,mid,tree[rt].lson,k);
else tree[rt].rson=change_add(nl,nr,mid+1,r,tree[rt].rson,k);
pushup(rt);
return rt;
}
int merge(int x,int y,int l,int r){
if (!x) return y;
if (!y) return x;
if (l==r){
tree[x].val+=tree[y].val;
return x;
}
pushdown(x,l,r),pushdown(y,l,r);
int mid=(l+r)>>1;
tree[x].lson=merge(tree[x].lson,tree[y].lson,l,mid);
tree[x].rson=merge(tree[x].rson,tree[y].rson,mid+1,r);
return x;
}
int query(int nl,int l,int r,int rt){
if (!rt) return 0;
if (l==r) return tree[rt].val;
pushdown(l,r,rt);
int mid=(l+r)>>1,cur;
if (nl<=mid&&tree[rt].lson) cur=query(nl,l,mid,tree[rt].lson);
else if (nl>mid&&tree[rt].rson) cur=query(nl,mid+1,r,tree[rt].rson);
return cur;
}
void print(int l,int r,int rt){
if (l==r){
if (!rt) cout<<0<<endl;
else cout<<tree[rt].val<<' ';
return;
}
if (!tree[rt].lson) tree[rt].lson=++tot;
if (!tree[rt].rson) tree[rt].rson=++tot;
pushdown(l,r,rt);
int mid=(l+r)>>1;
print(l,mid,tree[rt].lson);
print(mid+1,r,tree[rt].rson);
}
void dfs2(int now,int fath){
if (leav[now]){
root[now]=change_assign(1,n,1,n,root[now],1);
}
for (int i=head[now];i;i=e[i].nxt){
int y=e[i].to;
if (y==fath) continue;
dfs2(y,now);
root[now]=merge(root[now],root[y],1,n);
}
int tmp=query(dep[now],1,n,root[now]);
cout<<"AT:"<<now<<' '<<tmp<<endl;
print(1,n,root[now]);
cout<<endl<<endl;
root[now]=change_add(1,dep[now],1,n,root[now],tmp);
}
signed main(){
n=read();
for (int i=1;i<=n;i++) lg[i]=log2(i)+1,lim[i]=1;
for (int i=1;i<n;i++){
int u=read(),v=read();
add_edge(u,v),add_edge(v,u);
}
dfs(1,0);
m=read();
for (int i=1;i<=m;i++){
int u=read(),v=read();
if (dep[u]>dep[v]) swap(u,v);
lim[v]=max(lim[v],dep[up_tree(v,dep[u]-dep[v]-1)]);
}
dfs2(1,0);
cout<<query(1,1,n,root[1])<<endl;
return 0;
}