CF1536F Omkar and Akmar 题解

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} ABABAB BABA ⋯ BA \text{BABA}\cdots \text{BA} BABABA

由于存在两种不同的串(开始为 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 s1 个位置中插入剩下的 n − s − 1 n-s-1 ns1 个空格,贡献为 ( s − 1 n − s − 1 ) {s-1 \choose n-s-1} (ns1s1)

  • 若最左侧( n n n 号位置)不为空格,那么最右侧可以是空格,方案数为 ( s n − s ) {s \choose n-s} (nss)

得到答案:

∑ 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) 2i2i!((ni1i1)+(nii))

阶乘逆元预处理,然后 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值