a,b是签到题。
一个集合由N个整数组成,请求出它的非空子集和的中位数。(\(N<=2000\) \(A_i<=2000\))
发现所有的子集和是关于所有数的和对称的。即有 \(X\) 则有 \(\sum{A_i} - X\),于是通过背包优化的bitset算出所有能拼出的数,从 $ \frac{\sum{A_i}}{2}$ 从小往大选第一个即可。
多组询问。每个询问给定四个整数,\(A,B,C,D\),求一个满足这个条件的字符串:
- 长度为\(A+B\),由\(A\)个字符
A
和·\(B\)个字符B
构成。 - 在此基础上,连续的相同字符个数的最大值最小
- 在此基础上,字典序最小
输出这个字符串的第\(C\)位到第\(D\)位。
一个简单的分类讨论。题解讲的较清晰。没啥价值。
-
[AGC020E] Encoding Subsets
我们定义一个01
串的压缩是满足如下方式的字符串变化过程: -
\(0\rightarrow 0,1\rightarrow 1\)
-
如果 \(A\rightarrow P,B\rightarrow Q\) 合法,那么 \(A+B\rightarrow P+Q\) 也合法(其中 \(+\) 代表字符串拼接)
-
如果 \(S=\underbrace{A+A+\cdots+A}_{n\text{个}(n\ge 2)}\),那么 \(S\rightarrow(A\times n)\) 也合法(其中
(
,)
,×
为字符,\(n\) 为数字,算作一个字符,即使其中有 \(0/1\))
我们同时定义 \(01\) 串 \(B\) 是 \(A\) 的子集当且仅当:
- \(|A|=|B|\)
- \(\forall B_i=1,A_i=1\)
现在给你一个 \(01\) 串 \(S\),问它所有的子集的合法变化结果数的总和为多少。
答案对 \(998244353\) 取模。
观察到括号是可以重复嵌套的,于是从左向右的线性DP不太可行,考虑区间DP。
令\(f_{i,j}\)为 i 到 j的方案数,\(g_{i,j}\)为将 i 到 j 划分进一个括号的方案数。
(不重不漏地统计括号匹配方案数,这是一个经典套路)
那么 \(f_{i,j}=\sum_{i\leq k\leq j} g_{i,k}\times f_{j+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\)有长度\(l_i\)。
每一条弧\(i\)随机均匀地放置在圆上:选择圆上的一个随机实点,然后出现一条以该点为中心的长度为\(l_i\)的弧。弧是独立放置的。例如,它们可以相互交叉或包含。
现在问你圆的每一个实点被至少一个弧覆盖的概率是多少?注意一条弧覆盖了它的两个端点。
(C<=50 N<=6)
众所周知,数据范围越小,题目越神秘。
此题首先是一个环DP,断环为链,将最长的线段的左端点作为起点。(这样不会出现某一条线段越过断点覆盖未被覆盖的环的情况)
对于求概率的DP,如果有确定点当然可以直接通过确定点DP,对于没有确定点的情况,因为概率和长度无关而之和长度或位置的相对关系有关,于是可以钦定一个相对大小关系来DP。
在此题上,我们可以钦定每条线段的小数部分的大小关系,并将每一段‘1’拆分为n段,小数排名为 i 的数只能放在每段‘1’中的第 i 个位置。
于是可以枚举大小关系(复杂度 \(n!\) ),每一种情况正常地做DP。
时间复杂度 \(2^n \times n! \times (n\times 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;
}