agc020 vp记录

a,b是签到题。


一个集合由N个整数组成,请求出它的非空子集和的中位数。(N<=2000 Ai<=2000

 
发现所有的子集和是关于所有数的和对称的。即有 X 则有 AiX,于是通过背包优化的bitset算出所有能拼出的数,从 Ai2 从小往大选第一个即可。


多组询问。每个询问给定四个整数,A,B,C,D,求一个满足这个条件的字符串:

  1. 长度为A+B,由A个字符A和·B个字符B构成。
  2. 在此基础上,连续的相同字符个数的最大值最小
  3. 在此基础上,字典序最小

输出这个字符串的第C位到第D位。

 
一个简单的分类讨论。题解讲的较清晰。没啥价值。


  • [AGC020E] Encoding Subsets
    我们定义一个 01 串的压缩是满足如下方式的字符串变化过程:

  • 00,11

  • 如果 AP,BQ 合法,那么 A+BP+Q 也合法(其中 + 代表字符串拼接)

  • 如果 S=A+A++An(n2),那么 S(A×n) 也合法(其中 (, ), × 为字符,n 为数字,算作一个字符,即使其中有 0/1

我们同时定义 01BA 的子集当且仅当:

  • |A|=|B|
  • Bi=1,Ai=1

现在给你一个 01S,问它所有的子集的合法变化结果数的总和为多少。

答案对 998244353 取模。
 
观察到括号是可以重复嵌套的,于是从左向右的线性DP不太可行,考虑区间DP。
fi,j为 i 到 j的方案数,gi,j为将 i 到 j 划分进一个括号的方案数。
(不重不漏地统计括号匹配方案数,这是一个经典套路)
那么 fi,j=ikjgi,k×fj+1,k
记忆化搜索。

代码在这里
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int mod=998244353;
inline void ad(int &x,int y){return x+=y,x=(x>=mod)?x-mod:x,void();}
inline int mu(int x,int y){return 1ll*x*y%mod;}
unordered_map<string,int>f,g;
string I;
int getf(string s);
int getg(string s);
int getf(string s){
	if(s=="")return 1;
	if(f.count(s))return f[s];
	int len=s.length(),ansn=0;
	for(int i=1;i<=len;i++)ad(ansn,mu(getg(s.substr(0,i)),getf(s.substr(i,len-i+1))));
	return f[s]=ansn;
}
int getg(string s){
	if(s=="")return 1;
	if(s=="0")return 1;
	if(s=="1")return 2;
	if(g.count(s))return g[s];
	int ansn=0,len=s.length();
	for(int d=1;d<len;d++){
		if(len%d!=0)continue;
		string t="";
		for(int i=0;i<d;i++){
			bool flg=true;
			for(int j=i;j<len;j+=d)flg&=s[j]-'0';
			t=t+(char)('0'+flg);
		}
		ad(ansn,getf(t));
	} 
	return g[s]=ansn;
}
signed main(){
	cin>>I;
	printf("%lld\n",getf(I));
	return 0;
}

你有一个长度为C的圆,你在上面画了N个弧。弧i有长度li

每一条弧i随机均匀地放置在圆上:选择圆上的一个随机实点,然后出现一条以该点为中心的长度为li的弧。弧是独立放置的。例如,它们可以相互交叉或包含。

现在问你圆的每一个实点被至少一个弧覆盖的概率是多少?注意一条弧覆盖了它的两个端点。
(C<=50 N<=6)
 
众所周知,数据范围越小,题目越神秘。
此题首先是一个环DP,断环为链,将最长的线段的左端点作为起点。(这样不会出现某一条线段越过断点覆盖未被覆盖的环的情况)
对于求概率的DP,如果有确定点当然可以直接通过确定点DP,对于没有确定点的情况,因为概率和长度无关而之和长度或位置的相对关系有关,于是可以钦定一个相对大小关系来DP。
在此题上,我们可以钦定每条线段的小数部分的大小关系,并将每一段‘1’拆分为n段,小数排名为 i 的数只能放在每段‘1’中的第 i 个位置。
于是可以枚举大小关系(复杂度 n! ),每一种情况正常地做DP。
时间复杂度 2n×n!×(n×c)2

代码在这里
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
int n,c,l[60];
double f[510][510],ans,cnt;
signed main(){
	n=rd(),c=rd();
	for(int i=0;i<n;i++)l[i]=rd();
	sort(l,l+n);
	while(true){
		for(int i=0;i<=n*c;i++)for(int s=0;s<(1<<n-1);s++)f[i][s]=0;
		f[l[n-1]*n][0]=1;
		for(int i=1,p;i<=n*c;i++){
			if((p=i%n-1)<0)continue;
			for(int j=i;j<=n*c;j++){
				for(int s=0;s<(1<<n-1);s++)if(~s>>p&1)f[min(c*n,max(j,i+l[p]*n))][s|(1<<p)]+=f[j][s];
			}
		}
		ans+=f[c*n][(1<<n-1)-1],cnt++;
		if(!next_permutation(l,l+n-1))break;
	}
	printf("%.13lf\n",ans/cnt/pow(c,n-1));
	return 0;
}

你以为这就结束了吗?这并不是最优复杂度!
有另一种思路,对于每一段1,如果我们不钦定相对大小关系而是直接DP,这样是会有精度问题的。然而如果对每一段‘1’划分得越多精度就越高。令分段数为 m,有一个结论:答案关于分段数 m 是一个 n 次多项式。因为线段总数才 n 条,次数不可能更高(可以感性理解一下)。于是我们只需要 n+1 个点叉出多项式,把 m 等于 c, 2c ,···,(n+1)c带入求值即可。
最终的答案是:

ans=limmf(m)mn

上面这东西就是第 n 项的系数。
总复杂度是 2nn4L4

posted @   flywatre  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示