zoukankan      html  css  js  c++  java
  • Codeforces Round #663 Div2 A~D 题解

    本场链接: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),可以发现棋盘就是一个长条,由于范围的原因,只可能有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;
    }
    
  • 相关阅读:
    ubuntu安装jdk的两种方法
    LeetCode 606. Construct String from Binary Tree (建立一个二叉树的string)
    LeetCode 617. Merge Two Binary Tree (合并两个二叉树)
    LeetCode 476. Number Complement (数的补数)
    LeetCode 575. Distribute Candies (发糖果)
    LeetCode 461. Hamming Distance (汉明距离)
    LeetCode 405. Convert a Number to Hexadecimal (把一个数转化为16进制)
    LeetCode 594. Longest Harmonious Subsequence (最长的协调子序列)
    LeetCode 371. Sum of Two Integers (两数之和)
    LeetCode 342. Power of Four (4的次方)
  • 原文地址:https://www.cnblogs.com/HotPants/p/13475537.html
Copyright © 2011-2022 走看看