ABC399

AtCoder Beginner Contest 399(C~F)

C - Make it Forest

A simple undirected graph F is called a forest if and only if F does not contain any cycle.

用并查集计数即可.

D - Switch Seats

将每个数的两个位置存下来 , 根据条件直接数即可.

set计数比较好写.

E - Replace

可以转化为一个图论问题 , 要将s中的a转化为t中的b可以看成一条边\(a\rightarrow b\) .

若出度大于1 , 显然无解.

否则若有环 , 如\(a\rightarrow b , b\rightarrow c,c\rightarrow a\) , 3次操作显然不行 , 如果我们先将\(c\rightarrow d\) , 就没有环了 , 可以做到.

但是有可能c没有可以转化的d了 .

我们需要的是在s没出现的一个字母 , 如果所有26个字母都在环内显然就无解 , 否则计算环的数目 .

这里的环不能有其他的边 , 如\(a\rightarrow b , b\rightarrow c,c\rightarrow a,d\rightarrow a\) ,则不需要统计.

最后的答案为除自环外的边数加上纯环的数量.

纯环可以用并查集找到.

void solve() {
  int n;cin>>n;
  string s,t;cin>>s>>t;
  if (s==t) {
    cout<<0<<"\n";
    return ;
  }
  int ans=0;
  for (int i=1;i<=26;++i) fa[i]=i,ok[i]=1;
  for (int i=0;i<n;++i) {
    int x=s[i]-'a'+1,y=t[i]-'a'+1;
    if (to[x] and to[x]!=y) {
      cout<<"-1\n";
      return ;
    }
    if (to[x]) continue;
    merge(x,y);
    to[x]=y;
    in[y]++;
    out[x]++;
    if (x!=y) ans++;
  }
  for (int i=1;i<=26;++i) {
    sz[find(i)]++;
    if (out[i]==1 and in[i]==1) continue;
    ok[find(i)]=0;
  }
  int sum=0;
  for (int i=1;i<=26;++i) {
    if (i==find(i) and ok[i]) {
      sum+=sz[i];
      if (sz[i]>1) ans++;
    }
  }
  if (sum==26) ans=-1;
  cout<<ans<<"\n";
}

F - Range Power Sum

First

\[\sum_{1\le l \le r\le N} \left(\sum_{l\le i \le r}A_i\right)^k \]

注意到\(k\)比较小 , 考虑直接展开. 看看能否用前缀和去优化. 一般通过交换求和次序 , 然后将一些项移出和式 , 得到一些能够预处理的东西 , 就可以消除一重和式 .

\[\begin{aligned} &\sum_{1\le l \le r\le N} \left(\sum_{l\le i \le r}A_i\right)^k\\ =&\sum_{1\le l \le r\le N} (p_r-p_{l-1}) ^k\\ =&\sum_{1\le l \le r \le N}\left( \sum_{0\le t\le k} (-1)^{k-t}\binom{k}{t} p_r^{t} p_{l-1}^{k-t} \right)\\ =&\sum_{1\le r \le N} \sum_{1\le l \le r} \left( \sum_{0\le t\le k} (-1)^{k-t}\binom{k}{t} p_r^{t} p_{l-1}^{k-t} \right)\\ =&\sum_{0\le t\le k} (-1)^{k-t}\binom{k}{t} \sum_{1\le r\le N}p_r^{t} \sum_{1\le l \le r} p_{l-1}^{k-t} \end{aligned} \]

这样显然可算了. 通过预处理 , 时间复杂度为\(O(NK)\)

需要注意一点 : 在组合数学中 , 我们一般可以认为\(0^0=1\) .

Second

转化一下题目: \(A_i\)表示一个盒子\(i\)里面有\(A_i\)个球

分为两步

1.选择一个区间

2.为这个区间的的球贴\(k\)个标签的方法数

由于k的数据比较小 , 可以考虑dp

dp[i][j][k]枚举到\(i\) , 确定了j 个边界 , 贴了k个标签 的方法数.

对于每一个位置有3种操作

1.什么都不做dp[i+1][j][k]+=dp[i][j][k]

2.设为边界dp[i][j+1][k]+=dp[i][j][k]

3.打上p个标签\(dp[i+1][1][k+p]+=\binom{K-k}{p}A_i^p dp[i][1][k]\)

dp[0][0][0]=1;
  int ans=0;
  for (int i=0;i<=n;++i) {
    for (int k=0;k<=K;++k) {
      for (int j=0;j<=2;++j) {
        dp[i+1][j][k]+=dp[i][j][k],dp[i+1][j][k]%=mod;
        dp[i][j+1][k]+=dp[i][j][k],dp[i][j+1][k]%=mod;
      }
      for (int p=1;p<=K-k;++p) dp[i+1][1][k+p]+=C(K-k,p)*a[i+1][p]%mod*dp[i][1][k]%mod,dp[i+1][1][k+p]%=mod;
    }
  }
  cout<<dp[n][2][K]<<"\n";

由当前位置向后更新会比较好写 , 这样更新不容易出错 , 并且开大数组不用管边界.

Third

想办法减掉一维边界 , 比如先固定一维 , 这里固定右边界

\[\sum_{1\le r \le n} \sum_{1 \le l \le r} \left(\sum_{l \le i \le r} A_i\right)^k \]

能否快速计算右边的东西呢?

\[F(x,k)=\sum_{1 \le l \le x} \left(\sum_{l \le i \le x} A_i\right)^k \]

看上去似乎可以递推

\[\begin{aligned} F(x,k)&=\sum_{1 \le l \le x} \left(\sum_{l \le i \le x} A_i\right)^k\\ &=\sum_{1\le l \le x}\left(\sum_{l \le i \le x-1} A_i +A_x\right)^k\\ &=A_{x}^{k}+\sum_{1\le l \le x-1}\sum_{0\le p \le k} \binom{k}{p} \left(\sum_{l \le i \le x-1} A_i \right)^{p} A_x^{k-p}\\ &=A_{x}^{k}+\sum_{0\le p \le k} \binom{k}{p}A_x^{k-p} \sum_{1\le l \le x-1}\left(\sum_{l \le i \le x-1} A_i \right)^{p} \\ &=A_{x}^{k}+\sum_{0\le p \le k} \binom{k}{p}A_x^{k-p} F(x-1,p) \\ \end{aligned} \]

最后的答案为

\[\sum_{1\le x \le n} F(x,k) \]

posted @ 2025-04-06 15:38  _lull  阅读(10)  评论(0)    收藏  举报