AtCoder Grand Contest 020&021

020E Encoding Subsets

题目描述

点此看题

解法

首先考虑对于某个固定的方案如何计算,设 \(f(l,r)\) 表示将区间 \([l,r]\) 编码的方案数,\(g(l,r)\) 表示将区间 \([l,r]\) 编码成单个字符或由一个括号括起来的方案数,转移考虑将一段前缀编码:

  • \(f(l,r)=\sum_{k=l}^{r} g(l,k)\cdot f(k+1,r)\)
  • \(g(l,r)=\sum_{d=1}^{r-l} f(l,l+d-1)\),其中 \(d\) 是字符串 \(s[l...r]\) 的循环节。

那么如果要求子集怎么做呢?我们设 \(f(S)/g(S)\) 表示字符串 \(S\) 及其子集的答案之和,然后类似上面的转移即可,注意取出循环节时需要取 \(\tt and\)(因为都是 \(1\) 循环节这一位才可能是 \(1\)),时间复杂度算不来,用记忆化搜索就可以跑过。

总结

这种嵌套类型是常见的,如果转移可以表示成两个形式不同子问题的嵌套,那么可以尝试使用分步 \(dp\)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
using namespace std;
const int MOD = 998244353;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
string s;map<string,int> mp[2];
void add(int &x,int y) {x=(x+y)%MOD;}
int g(string s) ;
int f(string s)
{
	if(s=="") return 1;
	if(mp[0].count(s)) return mp[0][s];
	int r=0,n=s.length();
	for(int i=1;i<=n;i++)
		add(r,1ll*g(s.substr(0,i))
		*f(s.substr(i,n-i+1))%MOD);
	return mp[0][s]=r;
}
int g(string s)
{
	if(s=="" || s=="0") return 1;
	if(s=="1") return 2;
	if(mp[1].count(s)) return mp[1][s];
	int r=0,n=s.length();
	for(int d=1;d<n;d++)
	{
		if(n%d) continue;
		string t="";
		for(int i=0;i<d;i++)
		{
			int x=1;
			for(int j=i;j<n;j+=d)
				x&=(s[j]=='1');
			t+=(x+'0');
		}
		add(r,f(t));
	}
	return mp[1][s]=r;
}
signed main()
{
	cin>>s;
	printf("%d\n",f(s));
}

021E Ball Eat Chameleons

题目描述

点此看题

解法

首先有一个关键的 \(\tt obseervation\) 是,变色龙最后显红色的充要条件是:吃的红球数大于蓝球数;或者红球数等于蓝球数,并且最后一个吃的是蓝球。

这样 \(n=1\) 的情况就很好解决了,下一步我们来思考如果知道颜色序列如果确定最优分配方案。考虑判定恰好 \(n\) 条龙变红色等价于判定 \(\geq n\) 条龙变红色(但是要求红球数\(>\)蓝球数,所以 \(R=B\) 的情况可以化归到 \((R,R-1)\) 的情况)

那么最优方案就是取出所有的子序列 AB,剩下的颜色可以让恰好 \(R-B\) 条变色龙变色,所以子序列 AB 的数量必须 \(\geq n-(R-B)\),那么我们可以计算出未匹配的 B 至多有 \(B-(n-(R-B))=R-n\) 个。

那么未匹配的 B 等价于,把 A 看成 \(-1\)B 看成 \(+1\),前缀和的最大值 \(\leq R-n\),这样我们就解决了判定问题。

现在计数问题就容易解决了,我们枚举 \(R\),那么问题转化成从 \((0,0)\) 走到 \((R,B)\),每次可以向右走或者向上走,问不能越过 \(y=x+(R-n)\) 的方案数,套用公式可以得到:

\[{R+B\choose R}-{R+B\choose 2R-n+1} \]

时间复杂度 \(O(k)\)

彩蛋:这题在我还没仔细分析的时候就觉得是卡特兰数之类的组合意义,所以我的数感是不是越来越好了

#include <cstdio>
const int M = 1000005;
const int MOD = 998244353;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,k,ans,fac[M],inv[M];
void init(int n)
{
	fac[0]=inv[0]=inv[1]=1;
	for(int i=2;i<=n;i++) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
	for(int i=2;i<=n;i++) inv[i]=inv[i]*inv[i-1]%MOD;
	for(int i=1;i<=n;i++) fac[i]=i*fac[i-1]%MOD;
}
int C(int n,int m)
{
	if(m<0 || n<m) return 0;
	return fac[n]*inv[n-m]%MOD*inv[m]%MOD;
}
void add(int &x,int y) {x=(x+y)%MOD;}
signed main()
{
	n=read();k=read();init(k);
	if(k<n) {puts("0");return 0;}
	for(int R=0;R<=k;R++)
	{
		int B=k-R;
		if(R<B) continue;
		if(R==B) B--;
		add(ans,MOD+C(R+B,R)-C(R+B,2*R-n+1));
	}
	printf("%lld\n",ans);
}
posted @ 2022-03-13 15:23  C202044zxy  阅读(115)  评论(0编辑  收藏  举报