ABC263

ABC263

img

tasks

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

二分+计算几何?

img

posted @ 2022-10-16 22:47  AIskeleton  阅读(19)  评论(0编辑  收藏  举报