ABC263
ABC263
VP
score | A | B | C | D | E | F | G |
---|---|---|---|---|---|---|---|
105:02 | 0:24 | 4:11 | 15:03 | 38:49 | 85:02 | ||
+4 | +1 | +1 | +2 |
rk:\(316th\)
perf:\(\color{Gold}{2012}\)
A、B
忽略。
C
就是记一下 next_permutation
这个函数。
或者爆搜也行。
code
void work(){
cin>>n>>m;vi f;
L(i,1,n) f.pb(0);
L(i,1,m-n) f.pb(1);
do{
L(i,0,m-1) i(!f[i])
cout<<i+1<<" ";
cout<<'\n';
}while(next_permutation(all(f)));
}
D
稍微分类讨论一下,把问题转化成更为更加可解的情况。
- 如果整个数组 \(a\) 都被覆盖
此时答案显然是 \(\min(L,R) \cdot n\)。
- 如果数组不是完全覆盖
中间必然有一段 \(\left[ l,r \right]\) 没有被改变。
考虑一个前缀和数组 \(s_i = \sum_{j=1}^i a_j\),此时的答案为 $$s_r -s_{l-1}+R \times (n-r) +L \times (l-1)$$
所以枚举左端点 \(l\) 和后缀最小和 \(s_{l} + R \times (n-r)\) 并更新答案即可。
code
void work(){
cin>>n>>x>>y;
L(i,1,n) cin>>a[i],s[i]=a[i]+s[i-1];
R(i,n,0){
sum=min(sum,s[i]+y*(n-i));
ans=min(ans,x*i-s[i]+sum);
}cout<<ans;
}
E
期望 DP,倒推。
设 \(f_i\) 表示在位置 \(i\) 时的期望投掷次数。
对于 DP 的初始状态,显然有 \(f_n=0\)。
当前转移到的 \(f_i\),可以推得状态转移方程:$$f_i =1+ \dfrac{1}{A_i} \sum\limits_{j=i}^{i+A_i} f_j$$
但是这样转移的过程中会产生自环,因为等式左右两边都有 \(f_i\) 这一项。
所以移项消掉右边的 \(f_i\),得到:$$f_i = \dfrac{A_i+1+\sum_{j=i+1}^{i+A_i} f_j}{A_i}$$
那个 \(\sum\) 的部分显然可以用后缀和优化转移,这样就解决了。
时间复杂度:\(O(n \log_2 \bmod)\)
code
void work(){
cin>>n;vector<Z>a(n*2,0),f(n*2,0),s(n*2,0);
L(i,1,n-1) cin>>a[i];f[n]=0;
R(i,n-1,1){
Z res=s[i+1]-s[i+1+a[i].val()];
f[i]=(a[i]+1+res)/a[i];s[i]=s[i+1]+f[i];
}cout<<f[1];
}//取模结构体Z可以翻我的缺省源
F
题目相当于给出了一棵 \(n+1\) 层的满二叉树。
最底下一层刚好对应 \(1,2,\dots 2^n\) 的人。
从最上层开始向下转移。
枚举所有左右子树胜利的情况,取其中 \(\max\) 值即可。
同时记录当前状态下的连胜场次数,遍历到最底层时直接返回贡献即可。
显然可以记忆化搜索。
时间复杂度:\(O(n\cdot 2^n)\)。
code
const int N=(1<<17)+100,INF=1e9;
int n,c[N][20],f[N][20];
int dfs(int p,int w){
if(p>=(1<<n)) return c[p^(1<<n)][w];if(~f[p][w]) return f[p][w];
return f[p][w]=max(dfs(lc,w+1)+dfs(rc,0),dfs(lc,0)+dfs(rc,w+1));
}void work(){
cin>>n;L(i,0,((1<<n)-1)) L(j,1,n) cin>>c[i][j];
me(f,-1);cout<<dfs(1,0);
}
G
蒟蒻只会网络流做法。
本题的网络流建模方法其实不止一种,这里会提到两种。
建模一
考虑拆点,每个 \(a_i\) 对应的点 \(i\) 拆成入点 \(i\) 和出点 \(i+n\),分别向源点和汇点连容量为 \(b_i\) 的边。
枚举所有组合情况,如果满足和为质数,其入点向出点连边即可。
最后结果除二就是答案。
code
void work(){
cin>>n;vi a(n),b(n);
F.st=n*2+1,F.ed=n*2+2;
es(10000000);
L(i,0,n-1){
cin>>a[i]>>b[i];
F.I(F.st,i,b[i]);
F.I(i+n,F.ed,b[i]);
}L(i,0,n-1) L(j,0,n-1)
if(f[a[i]+a[j]]) F.I(i,j+n,INF);
cout<<F.dinic()/2;
}
建模二
赛时做法,无比愚蠢。
先忽略 \(1\),不拆点,按上面的方法建图。
得到最大流之后引入新的汇点,把之前的汇点和 \(1\) 连上去。
再次跑出最大流,加上剩余的除二即可。
但确实愚蠢了不少。
code
void work(){
cin>>n;vi a(n),b(n);
L(i,0,n-1){
cin>>a[i]>>b[i];
if(a[i]==1) c1=b[i],b[i]=0;
}m=*max_element(all(a))*2;
es(10000000);
L(i,0,n-1)
if(a[i]&1) F.I(i,n+1,b[i]);
else{
F.I(n,i,b[i]);
L(j,0,n-1) if((a[j]&1)&&f[a[i]+a[j]])
F.I(i,j,INF);
}
F.st=n,F.ed=n+1;a1=F.dinic();
F.I(n+2,n+1,c1);L(i,0,n-1)
if(a[i]%2==0&&f[a[i]+1]) F.I(i,n+2,INF);
a2=a1+F.dinic();
ans=a2+(c1-(a2-a1))/2;
cout<<ans;
}
Ex
二分+计算几何?