1948.Educational Codeforces Round 163 - sol

202403

补题效率低下。


场上发挥并不是很好,

A ~ E 都是简单的,而场上没有去推 F 的式子,只是找了找规律,然后发现是一个不可做的东西就下播了。

如果直接推式子就会很快地做出来,还是非常可惜。


A. Special Characters

You are given an integer \(n\).

Your task is to build a string of uppercase Latin letters. There must be exactly \(n\) special characters in this string. Let's call a character special if it is equal to exactly one of its neighbors.

For example, there are \(6\) special characters in the AAABAACC string (at positions: \(1\), \(3\), \(5\), \(6\), \(7\) and \(8\)).

Print any suitable string or report that there is no such string.

\(1 \le n \le 50\)

直接 AABBAABBAA …… 即可。代码


B. Array Fix

You are given an integer array \(a\) of length \(n\).

You can perform the following operation any number of times (possibly zero): take any element of the array \(a\), which is at least \(10\), delete it, and instead insert the digits that element consisted of in the same position, in order they appear in that element.

For example:

  • if we apply this operation to the \(3\)-rd element of the array \([12, 3, 45, 67]\), then the array becomes \([12, 3, 4, 5, 67]\).
  • if we apply this operation to the \(2\)-nd element of the array \([2, 10]\), then the array becomes \([2, 1, 0]\).

Your task is to determine whether it is possible to make \(a\) sorted in non-descending order using the aforementioned operation any number of times (possibly zero). In other words, you have to determine if it is possible to transform the array \(a\) in such a way that \(a_1 \le a_2 \le \dots \le a_k\), where \(k\) is the current length of the array \(a\).

\(2 \le n\)

有一个类 dp 的贪心做法吧。

首先最后一个数越小越优,所以我们直接记录前 \(i\) 个数满足条件,最后一个的值最小是多少就完成了。代码


C. Arrow Path

There is a grid, consisting of \(2\) rows and \(n\) columns. The rows are numbered from \(1\) to \(2\) from top to bottom. The columns are numbered from \(1\) to \(n\) from left to right. Each cell of the grid contains an arrow pointing either to the left or to the right. No arrow points outside the grid.

There is a robot that starts in a cell \((1, 1)\). Every second, the following two actions happen one after another:

  1. Firstly, the robot moves left, right, down or up (it can't try to go outside the grid, and can't skip a move);
  2. then it moves along the arrow that is placed in the current cell (the cell it ends up after its move).

Your task is to determine whether the robot can reach the cell \((2, n)\).

\(2 \le n \le 2 \times 10^5\)

直接用某种图论最短路算法模拟即可。代码


D. Tandem Repeats?

You are given a string \(s\), consisting of lowercase Latin letters and/or question marks.

A tandem repeat is a string of an even length such that its first half is equal to its second half.

A string \(a\) is a substring of a string \(b\) if \(a\) can be obtained from \(b\) by the deletion of several (possibly, zero or all) characters from the beginning and several (possibly, zero or all) characters from the end.

Your goal is to replace each question mark with some lowercase Latin letter in such a way that the length of the longest substring that is a tandem repeat is maximum possible.

\(1 \le |S| \le 5000\)

首先,枚举一下每一个长度是显然的,然后你就容易发现如果当前一半的长度为 \(k\),那么 \(i\)\(i+k\) 相等的对数最大连续长度至少为 \(k\)

于是你就可以做了,时间复杂度 \(\mathcal O(n^2)\)代码


E. Clique Partition

You are given two integers, \(n\) and \(k\). There is a graph on \(n\) vertices, numbered from \(1\) to \(n\), which initially has no edges.

You have to assign each vertex an integer; let \(a_i\) be the integer on the vertex \(i\). All \(a_i\) should be distinct integers from \(1\) to \(n\).

After assigning integers, for every pair of vertices \((i, j)\), you add an edge between them if \(|i - j| + |a_i - a_j| \le k\).

Your goal is to create a graph which can be partitioned into the minimum possible (for the given values of \(n\) and \(k\)) number of cliques. Each vertex of the graph should belong to exactly one clique. Recall that a clique is a set of vertices such that every pair of vertices in it are connected with an edge.

Since BledDest hasn't really brushed his programming skills up, he can't solve the problem "given a graph, partition it into the minimum number of cliques". So we also ask you to print the partition itself.

\(2 \le n \le 40\)

不要被数据小的东西误解了复杂度——这可能是给 checker 用的。


首先,很容易想到一定是一段一段地最优,

而我们容易猜到一个最长的长度就是 \(k\),为什么(你其实不需要知道)呢?

我也不知道。(实际上推一推也是好推的)

然后就到了这道题的关键之处——如何构造?

这里给出一种很好的做法——打表!!!

好,你做完了,稍微打一个表字典序最小的那个序列就有一些规律,很容易发现。

这样就线性做完了。代码


F. Rare Coins

There are \(n\) bags numbered from \(1\) to \(n\), the \(i\)-th bag contains \(a_i\) golden coins and \(b_i\) silver coins.

The value of a gold coin is \(1\). The value of a silver coin is either \(0\) or \(1\), determined for each silver coin independently (\(0\) with probability \(\frac{1}{2}\), \(1\) with probability \(\frac{1}{2}\)).

You have to answer \(q\) independent queries. Each query is the following:

  • \(l\) \(r\) — calculate the probability that the total value of coins in bags from \(l\) to \(r\) is strictly greater than the total value in all other bags.

\(1 \le n,q \le 3 \times 10^5,\sum a_i,\sum b_i \le 10^6\)

组合数好题。


首先正常地想,对于银币,一共有 \(2^{b[n]}\) 种不同的情况,这是非常好理解的,每一个有两种情况嘛。

而这里,由于询问的是区间,我们把 \(a,b\) 两个数组都进行了前缀和的处理。

现在来考虑每一次做的事情:

  • \(a\) 的差值是一定的,我们假设里面与外面的差值为 \(c\)
  • \(b\) 的差值是不定的,如果里面有 \(i\) 个被选成了 \(1\),外面有 \(j\) 个选成 \(1\),如果满足条件,则 \(j-i \lt c\)

这些东西能表明什么呢?


我们假设里面银币个数为 \(A\),外面银币个数为 \(B\),则合法的方案数是:

\[\begin{align} & \displaystyle \sum_{i=0}^A \sum_{j=0}^B [j-i \lt c] \binom A i \binom B j \\ = & \displaystyle \sum_{i=0}^A \sum_{j=0}^B [i-j \gt -c] \binom A i \binom B j \end{align} \]

容易发现,如果直接把求和的区间改一下,就变成了一个没有系数的下指标求和,

《具体数学》上面声称了它没有封闭形式。

所以怎么办呢?


我们考虑换一个思路,对于这个式子,我们去枚举 \(i,j\) 的差值 \(d\),于是有了以下推导:

\[\begin{align} & \displaystyle \sum_{i=0}^A \sum_{j=0}^B [i-j \gt -c] \binom Ai \binom Bj \\ = & \displaystyle \sum_{d=-c+1} \sum_{j=0}^B \binom{A}{j+d} \binom Bj \end{align} \]

由于 \(A,B\) 是非负整数,所以我们可以使用对称公式,进一步就可以对后面的式子进行 范德蒙德卷积

\[\begin{align} = & \displaystyle \sum_{d=-c+1} \sum_{j=0}^B \binom{A}{j+d} \binom{B}{B-j} \\ = & \displaystyle \sum_{d=-c+1} \sum_{j} \binom{A}{j+d} \binom{B}{B-j} \\ = & \displaystyle \sum_{d=-c+1} \binom{A+B}{B+d} \\ = & \displaystyle \sum_{d=B-c+1} \binom {A+B}{d} \end{align} \]

发现其实 \(A+B\) 就是整个序列银币的个数,也就是前缀和处理后的 \(b[n]\)

而整个式子就相当于求一个后缀和,这是完全可以预处理出来的!!!


于是我们就做完了,只要预处理一下 \(\displaystyle \sum_{d} \binom{b[n]}{d}\) 的后缀和就可以完成了。

代码实现是相当简单的,主要难点就在于转化去枚举差值。代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long

const int N=2e6+5;
int n,m,a[N],b[N];
ll fac[N],ifac[N],S[N],Inv;
const ll H=998244353;

ll binom(int n,int m){
  if(m<0||n<m) return 0;
  return fac[n]*ifac[m]%H*ifac[n-m]%H;
}

ll qpow(ll a,ll b){
  ll res=1;
  while(b){if(b&1) res=res*a%H;a=a*a%H,b>>=1;}
  return res;
}

ll calc(int c,int b){return S[max(0,b-c+1)]*Inv%H;}

int main(){
  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  cin>>n>>m;
  for(int i=1;i<=n;i++) cin>>a[i],a[i]+=a[i-1];
  for(int i=1;i<=n;i++) cin>>b[i],b[i]+=b[i-1];
  
  Inv=qpow((H+1)/2,b[n]);
  fac[0]=1;
  for(int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%H;
  ifac[N-1]=qpow(fac[N-1],H-2);
  for(int i=N-1;i>=1;i--) ifac[i-1]=1ll*ifac[i]*i%H;
  
  for(int i=N-2;i>=0;i--) S[i]=(binom(b[n],i)+S[i+1])%H;
  
  for(int i=1,l,r;i<=m;i++){
    cin>>l>>r;
    cout<<calc(a[r]-a[l-1]-(a[n]-a[r]+a[l-1]),b[n]-b[r]+b[l-1])<<' ';
  }
  return 0;
}

G. MST with Matching

You are given an undirected connected graph on \(n\) vertices. Each edge of this graph has a weight; the weight of the edge connecting vertices \(i\) and \(j\) is \(w_{i,j}\) (or \(w_{i,j} = 0\) if there is no edge between \(i\) and \(j\)). All weights are positive integers.

You are also given a positive integer \(c\).

You have to build a spanning tree of this graph; i. e. choose exactly \((n-1)\) edges of this graph in such a way that every vertex can be reached from every other vertex by traversing some of the chosen edges. The cost of the spanning tree is the sum of two values:

  • the sum of weights of all chosen edges;
  • the maximum matching in the spanning tree (i. e. the maximum size of a set of edges such that they all belong to the chosen spanning tree, and no vertex has more than one incident edge in this set), multiplied by the given integer \(c\).

Find any spanning tree with the minimum cost. Since the graph is connected, there exists at least one spanning tree.

\(1 \le n \le 20,1 \le c \le 10^6\)

简单题做不出来,降智哩。/kk


首先 \(n\) 的范围很小,不难想到用二进制去直接枚举。

而枚举什么呢?

枚举边集很明显是不合适的,而枚举点集又不好确定所选点的内部边,

所以我们考虑一种树上的方法——直接枚举点,每个点代表选择 它到它父亲 的边!!!


于是枚举了边集之后,发现构建 MST 就是非常简单的了,

因为一定不会有一条边连到都没选和都选了的点,这是简单的,稍微画一画图就知道了。

这样我们直接跑一次最小生成树就做完了,时间复杂度 \(\mathcal O(2^nn^2)\)代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back

const int N=1e3+3;
int n,m,fa[N],cnt;
ll ans=4e18,c,res;

struct edge{
  int u,v;
  ll w;
  edge(int A=0,int B=0,ll C=0){u=A,v=B,w=C;}
  bool operator <(const edge &a) const{return w<a.w;}
};
vector<edge> G;

int find(int x){
  if(x==fa[x]) return x;
  return fa[x]=find(fa[x]);
}

void merge(int u,int v){
  u=find(u),v=find(v);
  if(u!=v) fa[u]=v;
}

int main(){
  ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  cin>>n>>c;
  for(int i=1;i<=n;i++)
    for(int j=1,w;j<=n;j++){
      cin>>w;
      if(w&&i<j) G.pb(edge(i,j,w));
    }
  sort(G.begin(),G.end());
  
  for(int s=0;s<(1<<n);s++){
    cnt=res=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(auto i:G)
      if( (((s>>(i.u-1))&1)||((s>>(i.v-1))&1)) && find(i.u)!=find(i.v)){
        ++cnt,res+=i.w;
        merge(i.u,i.v);
      }
    if(cnt==n-1) ans=min(ans,res+__builtin_popcount(s)*c);
  }
  cout<<ans;
  return 0;
}

Conclusion

  1. 遇到一种思路不可求的问题我们可以尝试转化枚举方式,用 \(\Delta\) 去进行枚举。(F)
  2. 有关二进制枚举的题,分析怎么枚举更优!!!(G)
posted @ 2024-03-19 15:52  H_W_Y  阅读(16)  评论(0编辑  收藏  举报