agc020 vp记录
a,b是签到题。
一个集合由N个整数组成,请求出它的非空子集和的中位数。( )
发现所有的子集和是关于所有数的和对称的。即有 则有 ,于是通过背包优化的bitset算出所有能拼出的数,从 从小往大选第一个即可。
多组询问。每个询问给定四个整数,,求一个满足这个条件的字符串:
- 长度为,由个字符
A
和·个字符B
构成。 - 在此基础上,连续的相同字符个数的最大值最小
- 在此基础上,字典序最小
输出这个字符串的第位到第位。
一个简单的分类讨论。题解讲的较清晰。没啥价值。
-
[AGC020E] Encoding Subsets
我们定义一个01
串的压缩是满足如下方式的字符串变化过程: -
-
如果 合法,那么 也合法(其中 代表字符串拼接)
-
如果 ,那么 也合法(其中
(
,)
,×
为字符, 为数字,算作一个字符,即使其中有 )
我们同时定义 串 是 的子集当且仅当:
现在给你一个 串 ,问它所有的子集的合法变化结果数的总和为多少。
答案对 取模。
观察到括号是可以重复嵌套的,于是从左向右的线性DP不太可行,考虑区间DP。
令为 i 到 j的方案数,为将 i 到 j 划分进一个括号的方案数。
(不重不漏地统计括号匹配方案数,这是一个经典套路)
那么
记忆化搜索。
代码在这里
#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<=50 N<=6)
众所周知,数据范围越小,题目越神秘。
此题首先是一个环DP,断环为链,将最长的线段的左端点作为起点。(这样不会出现某一条线段越过断点覆盖未被覆盖的环的情况)
对于求概率的DP,如果有确定点当然可以直接通过确定点DP,对于没有确定点的情况,因为概率和长度无关而之和长度或位置的相对关系有关,于是可以钦定一个相对大小关系来DP。
在此题上,我们可以钦定每条线段的小数部分的大小关系,并将每一段‘1’拆分为n段,小数排名为 i 的数只能放在每段‘1’中的第 i 个位置。
于是可以枚举大小关系(复杂度 ),每一种情况正常地做DP。
时间复杂度
代码在这里
#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 是一个 n 次多项式。因为线段总数才 n 条,次数不可能更高(可以感性理解一下)。于是我们只需要 n+1 个点叉出多项式,把 m 等于 c, 2c ,···,(n+1)c带入求值即可。
最终的答案是:
上面这东西就是第 n 项的系数。
总复杂度是 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?