Processing math: 100%

Codeforces Round #663 Div2 A~D 题解

本场链接:Codeforces Round #663 Div2

闲话

手速场,但是D算一个分水岭.本场的话开到D就有200名.

A. Suborrays

原题大意:构造一个排列p满足任意一组连续子序列的或和不小于整段区间的长度,即pi|pi+1|...|pjji+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

原题大意:给定一个nm的棋盘,棋盘上有很多字母,代表在这个格子上只能往某个方向上移动.现要求棋盘上所有的点都能到达右下角的终点,问最少修改几个可以达成.输出次数.
数据范围:
1n,m100

思路

从棋盘上每一个点往外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\n",fres);
    }
    return 0;
}

C. Cyclic Permutations

原题大意:对于一个长度为n的排列,里面的每一个元素,向他左边最近且比他大的元素连边,以及右边最近且比他大的元素连边.问所有长度为n的排列里,至少包含一个简单环的排列有多少个.注意边是无向边.答案对109+7取模.
数据范围:
3n106

思路

模拟一下样例可以发现,如果有环出现的话,是有一个波谷才会出现的.而且这个波谷还得是连续的,就三位元素里出现的波谷.进一步可以发现至少存在一个环的条件很傻逼,换成全排列n!再抠掉不存在环的排列数就是答案.后者即整个排列里不存在一个波谷,也即一个单峰排列.并且是左上右下的,而且波峰必然是n.因此构造排列的方式就等价于说在n旁边插入元素,每个元素有两种选择,一共就是2n1.因此最后的答案就是n!2n1.由于不需要定点的查,所以一开始直接递推求阶乘,再套一个快速幂模板就轻松过掉本题了.
注意有减法操作,防范负数取模.

代码

#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

原题大意;给定一个nm的二进制矩阵.定义一个二进制矩阵是牛逼的,当且仅当所有的边长为偶数的正方形区域里和1的个数是奇数.现给定一个矩阵,问最少要修改其中的几个元素才能使它变成牛逼的二进制矩阵.
数据范围:
1nm106
1nm106

思路

这题看起来很牛逼,数据范围比较大.从数据范围来想的话,做法肯定不能暴力,而且就这个范围都不知道怎么开的下空间存.进而可以猜测一下是不是有范围问题.模拟一下数据之后可以发现当n,m都大于等于4的时候,整个矩阵必然无解.因为这样就存在一个44的正方形,而整个里面必然出现偶数个1,导致不符合条件.又由于是要满足所有的存在的可行方案都不能有,所以必然整个问题都无解.就可以将范围压下来了:两个边长至少有一个比4小.而且题目规定了nm.所以n3.
由于情况很少,可以直接讨论:
n=1显然不需要修改.
n=2,可以发现棋盘就是一个长条,由于范围的原因,只可能有2
2的正方形存在,从左到右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;
}
posted @   随处可见的阿宅  阅读(270)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· DeepSeek火爆全网,官网宕机?本地部署一个随便玩「LLM探索」
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 上周热点回顾(1.20-1.26)
· 【译】.NET 升级助手现在支持升级到集中式包管理
点击右上角即可分享
微信分享提示