• 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;
    }
    
  • 相关阅读:
    SpringMVC中利用@InitBinder来对页面数据进行解析绑定
    转型新零售必看:线下零售的运营模型
    主流CTR预估模型的演化及对比
    tensorflow创建自定义 Estimator
    构建分布式Tensorflow模型系列:CVR预估之ESMM
    tensorflow tfdbg 调试手段
    推荐系统算法学习(一)——协同过滤(CF) MF FM FFM
    CTR预估算法之FM, FFM, DeepFM及实践
    深度学习在美团搜索广告排序的应用实践
    Redis集群搭建最佳实践
  • 原文地址:https://www.cnblogs.com/HotPants/p/13475537.html
Copyright © 2020-2023  润新知