其他分享
首页 > 其他分享> > Codeforces Round #663 Div2 A~D 题解

Codeforces Round #663 Div2 A~D 题解

作者:互联网

本场链接:[Codeforces Round #663 Div2](https://codeforces.ml/contest/1391) # 闲话 手速场,但是D算一个分水岭.本场的话开到D就有200名.
# A. Suborrays 原题大意:构造一个排列$p$满足任意一组连续子序列的或和不小于整段区间的长度,即$p_i | p_{i+1} | ... | p_j \geq j - i + 1$
## 思路 显然$1,2,3,4,....,n$满足题意.
## 代码 ```C++ #include <bits/stdc++.h> using namespace std; typedef long long ll;
int main() { int T;scanf("%d",&T); while(T--) {         int n;scanf("%d",&n);         for(int i = 1;i <= n;++i)   printf("%d ",i);         puts(""); } return 0; } ```
# B. Fix You 原题大意:给定一个$n*m$的棋盘,棋盘上有很多字母,代表在这个格子上只能往某个方向上移动.现要求棋盘上所有的点都能到达右下角的终点,问最少修改几个可以达成.输出次数. 数据范围: $1 \leq n,m \leq 100$
## 思路 从棋盘上每一个点往外BFS拓展,并且记录经过了哪些点,如果最终到达了终点,就把所有经过的点特殊标记,如果以后的过程走到了这样的一个点,说明往后一定可以走到终点.除此之外,设立一个普通记录,即表示最终没有走到终点,但是这个点被拓展过了,不能额外的拓展.最后返回是否能到达终点,统计不能走到终点的个数,即为要修改的次数.
## 代码 ```C++ #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pii; #define x first #define y second const int N = 105; char g[N][N]; int n,m,st[N][N]; int bfs(int sta,int end) {     queue<pii> q;q.push({sta,end});     vector<pii> op;op.push_back({sta,end});     int ok = 0;     while(!q.empty())     {         auto t = q.front();q.pop();         int x = t.first,y = t.second;         if(st[x][y] == 2 || (x == n && y == m))         {             ok = 1;             break;         }         st[x][y] = 1;
        if(g[x][y] == 'R')         {             int a = x,b = y + 1;             if(b > m)   continue;             q.push({a,b});             op.push_back({a,b});         }         if(g[x][y] == 'D')         {             int a = x + 1,b = y;             if(a > n)   continue;             q.push({a,b});             op.push_back({a,b});         }     }     for(auto& t : op)   st[t.first][t.second] = 2;     return !ok; } int main() { int T;scanf("%d",&T); while(T--) {         scanf("%d%d",&n,&m);         memset(st,0,sizeof st);         for(int i = 1;i <= n;++i)   scanf("%s",g[i] + 1),getchar();         int fres = 0;         for(int i = 1;i <= n;++i)         {             for(int j = 1;j <= m;++j)             {                 if(!st[i][j])                 {                     int r = bfs(i,j);                     // if(r)    cout << i << " " << j << endl;                     fres += r;                 }             }         }         printf("%d\n",fres); } return 0; } ```
# C. Cyclic Permutations 原题大意:对于一个长度为$n$的排列,里面的每一个元素,向他左边最近且比他大的元素连边,以及右边最近且比他大的元素连边.问所有长度为$n$的排列里,至少包含一个简单环的排列有多少个.注意边是无向边.答案对$10^9+7$取模. 数据范围: $3 \leq n \leq 10^6$
## 思路 模拟一下样例可以发现,如果有环出现的话,是有一个波谷才会出现的.而且这个波谷还得是连续的,就三位元素里出现的波谷.进一步可以发现至少存在一个环的条件很傻逼,换成全排列$n!$再抠掉不存在环的排列数就是答案.后者即整个排列里不存在一个波谷,也即一个单峰排列.并且是左上右下的,而且波峰必然是$n$.因此构造排列的方式就等价于说在$n$旁边插入元素,每个元素有两种选择,一共就是$2^{n-1}$.因此最后的答案就是$n!-2^{n-1}$.由于不需要定点的查,所以一开始直接递推求阶乘,再套一个快速幂模板就轻松过掉本题了. 注意有减法操作,防范负数取模.
## 代码 ```C++ #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6+7,MOD = 1e9 + 7; ll fact[N]; void init() {     fact[0] = 1;     for(int i = 1;i < N;++i)         fact[i] = (fact[i - 1] * i) % MOD; } ll qmul(ll a, ll b, ll P) {   ll L = a * (b >> 25LL) % P * (1LL << 25) % P;   ll R = a * (b & ((1LL << 25) - 1)) % P;   return (L + R) % P; } ll qpow(ll a,ll b,ll p) {     ll res = 1 % p;     while(b)     {         if(b & 1)   res = qmul(res,a,p);         a = qmul(a,a,p);         b >>= 1;     }     return res; } int main() {     init();     int n;scanf("%d",&n);     ll res = ((fact[n] - qpow(2,n - 1,MOD)) % MOD + MOD) % MOD;     printf("%lld",res); return 0; } ```
# D. 505 原题大意;给定一个$n*m$的二进制矩阵.定义一个二进制矩阵是牛逼的,当且仅当所有的边长为偶数的正方形区域里和1的个数是奇数.现给定一个矩阵,问最少要修改其中的几个元素才能使它变成牛逼的二进制矩阵. 数据范围: $1 \leq n \leq m \leq 10^6$ $1 \leq n * m \leq 10^6$
## 思路 这题看起来很牛逼,数据范围比较大.从数据范围来想的话,做法肯定不能暴力,而且就这个范围都不知道怎么开的下空间存.进而可以猜测一下是不是有范围问题.模拟一下数据之后可以发现当$n,m$都大于等于4的时候,整个矩阵必然无解.因为这样就存在一个4*4的正方形,而整个里面必然出现偶数个1,导致不符合条件.又由于是要满足所有的存在的可行方案都不能有,所以必然整个问题都无解.就可以将范围压下来了:两个边长至少有一个比4小.而且题目规定了$n\leq m$.所以$n \leq 3$. 由于情况很少,可以直接讨论: ①$n=1$显然不需要修改. ②$n=2$,可以发现棋盘就是一个长条,由于范围的原因,只可能有2*2的正方形存在,从左到右DP就能解决这个问题. ③$n=3$,同②,只不过现在是三行. 这个时候思路基本就可以确定了,就是一个从左往右推的DP,由于情况相当的少,可以暴力枚举做掉这道题.下面讲一下DP过程
### 状态设计 状态:$f[i][j][k]$表示当前在$i$列,上面一个是$j$下面一个是$k$.并且将整个矩阵到$i$列为止都修改到合法的最小代价. 入口:枚举第一列的选择和不等的部分. 转移:枚举本列的四种情况.知道本列是什么状态,自然可以找出上一列合法的状态有哪些,直接拿过来用就可以了.并且还要加上本列有哪些不同. 出口:最后一列的答案最小者.
到这里,基本就可以写完第一个情况了.而第③个情况只是②多加了一行,本质是相同的.这个题唯一的难点就在于枚举很麻烦,本身并不算难.剩下的建议自己手推.代码实现的时候用引用可以减少一点码量,也可以看得更清晰.
## 代码 ```C++ #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 7,M = 5; ll f[N][2][2],dp[N][2][2][2]; char g[M][N]; int n,m; inline int cost(int x,int y,int c) {     int res = 0;     if(g[1][c] - '0' != x)  ++res;     if(g[2][c] - '0' != y)  ++res;     return res; } inline int cost(int x,int y,int z,int c) {     int res = 0;     if(g[1][c] - '0' != x)  ++res;     if(g[2][c] - '0' != y)  ++res;     if(g[3][c] - '0' != z)  ++res;     return res; } ll slove1() {     for(int i = 0;i <= 1;++i)         for(int j = 0;j <= 1;++j)             f[1][i][j] = cost(i,j,1);     for(int i = 2;i <= m;++i)     {         auto& p = f[i],&q = f[i - 1];         p[0][0] = min(q[0][1],q[1][0]) + cost(0,0,i);         p[0][1] = min(q[0][0],q[1][1]) + cost(0,1,i);         p[1][0] = min(q[0][0],q[1][1]) + cost(1,0,i);         p[1][1] = min(q[0][1],q[1][0]) + cost(1,1,i);            }     ll res = 1e18;     for(int i = 0;i <= 1;++i)         for(int j = 0;j <= 1;++j)             res = min(res,f[m][i][j]);     return res; } ll slove2() {     for(int i = 0;i <= 1;++i)         for(int j = 0;j <= 1;++j)             for(int k = 0;k <= 1;++k)                 dp[1][i][j][k] = cost(i,j,k,1);     for(int i = 2;i <= m;++i)     {         auto& p = dp[i],&q = dp[i - 1];         p[0][0][0] = min(q[1][0][1],q[0][1][0]) + cost(0,0,0,i);         p[0][0][1] = min(q[1][0][0],q[0][1][1]) + cost(0,0,1,i);         p[0][1][0] = min(q[0][0][0],q[1][1][1]) + cost(0,1,0,i);         p[1][0][0] = min(q[0][0][1],q[1][1][0]) + cost(1,0,0,i);         p[1][0][1] = min(q[0][0][0],q[1][1][1]) + cost(1,0,1,i);         p[1][1][0] = min(q[1][0][0],q[0][1][1]) + cost(1,1,0,i);         p[0][1][1] = min(q[0][0][1],q[1][1][0]) + cost(0,1,1,i);         p[1][1][1] = min(q[0][1][0],q[1][0][1]) + cost(1,1,1,i);     }     ll res = 1e18;     for(int i = 0;i <= 1;++i)         for(int j = 0;j <= 1;++j)             for(int k = 0;k <= 1;++k)                 res = min(dp[m][i][j][k],res);     return res; } int main() {     scanf("%d%d",&n,&m);     if(n >= 4 && m >= 4)    return puts("-1"),0;     for(int i = 1;i <= n;++i)   scanf("%s",g[i] + 1); if(n == 1)  return puts("0"),0; if(n == 2)  printf("%lld",slove1()); if(n == 3)  printf("%lld",slove2()); return 0; } ```

标签:int,题解,ll,663,Codeforces,long,leq,res,##
来源: https://www.cnblogs.com/HotPants/p/13475394.html