本场链接:Codeforces Round #663 Div2
闲话
手速场,但是D算一个分水岭.本场的话开到D就有200名.
A. Suborrays
原题大意:构造一个排列(p)满足任意一组连续子序列的或和不小于整段区间的长度,即(p_i | p_{i+1} | ... | p_j geq j - i + 1)
思路
显然(1,2,3,4,....,n)满足题意.
代码
#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拓展,并且记录经过了哪些点,如果最终到达了终点,就把所有经过的点特殊标记,如果以后的过程走到了这样的一个点,说明往后一定可以走到终点.除此之外,设立一个普通记录,即表示最终没有走到终点,但是这个点被拓展过了,不能额外的拓展.最后返回是否能到达终点,统计不能走到终点的个数,即为要修改的次数.
代码
#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
",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}).由于不需要定点的查,所以一开始直接递推求阶乘,再套一个快速幂模板就轻松过掉本题了.
注意有减法操作,防范负数取模.
代码
#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的时候,整个矩阵必然无解.因为这样就存在一个44的正方形,而整个里面必然出现偶数个1,导致不符合条件.又由于是要满足所有的存在的可行方案都不能有,所以必然整个问题都无解.就可以将范围压下来了:两个边长至少有一个比4小.而且题目规定了(nleq m).所以(n leq 3).
由于情况很少,可以直接讨论:
①(n=1)显然不需要修改.
②(n=2),可以发现棋盘就是一个长条,由于范围的原因,只可能有22的正方形存在,从左到右DP就能解决这个问题.
③(n=3),同②,只不过现在是三行.
这个时候思路基本就可以确定了,就是一个从左往右推的DP,由于情况相当的少,可以暴力枚举做掉这道题.下面讲一下DP过程
状态设计
状态:(f[i][j][k])表示当前在(i)列,上面一个是(j)下面一个是(k).并且将整个矩阵到(i)列为止都修改到合法的最小代价.
入口:枚举第一列的选择和不等的部分.
转移:枚举本列的四种情况.知道本列是什么状态,自然可以找出上一列合法的状态有哪些,直接拿过来用就可以了.并且还要加上本列有哪些不同.
出口:最后一列的答案最小者.
到这里,基本就可以写完第一个情况了.而第③个情况只是②多加了一行,本质是相同的.这个题唯一的难点就在于枚举很麻烦,本身并不算难.剩下的建议自己手推.代码实现的时候用引用可以减少一点码量,也可以看得更清晰.
代码
#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;
}