【题解】Codeforces 1072:Round #517 (Div. 2) ABCD

版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/83280181

我有毒

总结

  1. 值域有限的情况下构造,枚举初始值总是一个好方法。
  2. 先想简单情况,然后把复杂情况转化成简单情况。

A. Golden Plate 计算

给h*w的网格图,把最外面一圈染成黄色,第三圈,第五圈…一共染k圈,求最后的黄色格子数量。

直接计算,这道题甚至保证k不会超界

	int n=read(),m=read(),k=read();
	int ans = 0;
	while(k--)
	{
		ans += n*2+m*2-4;
		n-=4;
		m-=4;
	}
	printf("%d\n",ans );

B. Curiosity Has No Limits 构造

给定n,和长度为n-1的序列a,b(值域0,1,2,3),构造一个长度为n的序列t,使得
t i t i + 1 = a i t i & t i + 1 = b i . t_i|t_{i+1}=a_i且t_i\&t_{i+1}=b_i.

从值域入手,打表发现如果已知 t i , a i , b i t_i,a_i,b_i ,那么 t i + 1 t_{i+1} 要么不存在,要么唯一确定。枚举 t 1 t_1 ,依次判断能否构造出来。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 100016, MOD = 1000000007;

vector<int> save[4][4][4];
int sor[M],san[M],ans[M];
int main(void)
{
	for(int num=0;num<4;++num)
	{
		for(int aor=0;aor<4;++aor)
		{
			for(int aan=0;aan<4;++aan)
			{
				for(int i=0;i<4;i++)
					if(((num&i)==aan) && ((num|i)==aor))
						save[num][aor][aan].push_back(i);
			}
		}
	}
	int n = read();
	for(int i=1;i<n;++i)
		sor[i] = read();
	for(int i=1;i<n;++i)
		san[i] = read();

	int suc = 0;
	for(ans[1]=0;ans[1]<4;++ans[1])
	{
		int fail=0;
		for(int i=1;i<n;++i)
		{
			if(save[ ans[i] ][ sor[i] ][ san[i] ].size())
			{
				ans[i+1] = save[ ans[i] ][ sor[i] ][ san[i] ][0];
			}
			else
			{
				fail=1;
				break;
			}
		}
		if(!fail)
		{
			suc = 1;
			printf("YES\n");
			for(int i=1;i<=n;++i)
				printf("%d ",ans[i] );
			printf("\n");
			break;
		}
	}
	if(suc==0)
		printf("NO\n");
    return 0;
}

C. Cram Time 贪心

现在有无限本书可以读,读完第k本书需要花费k的时间。第一天有a时间,第二天有b时间,一本书不能在两天都读。问这两天最多能读完几本书,输出方案。

共a+b的时间,最多读前x本书,显然 x ( x + 1 ) / 2 &lt; = a + b x*(x+1)/2&lt;=a+b 。尽量填满a,然后剩下的给b即可。
一定可以填满a(或者剩下不超过 a + b x ( x + 1 ) / 2 a+b-x*(x+1)/2 的空缺),填的方法是每次填当前的最大值或者剩下的值,写函数多余了.

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 500016, MOD = 1000000007;

int tag[M];
void fill(int n,int m)
{
	if(n==0||m==0) return;
	if(n<=m)
	{
		tag[n]=1;
	}
	else
	{
		tag[m]=1;
		fill(n-m,m-1);
	}
}
void print(const vector<int>&vi)
{
	printf("%d\n",vi.size() );
	for(auto x:vi)
		printf("%d ",x );
	printf("\n");
}
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	ll a = read(), b = read(), t;
	for(t=0;t*(t+1)/2<=a+b;++t)
		; 
	--t;

	if(t==0)
	{
		printf("%d\n\n%d\n\n",0,0 );
	}
	else
	{
		fill(a,t);
		vector<int> resa,resb;
		for(int i=1;i<=t;++i)
			(tag[i]?resa:resb).push_back(i);
		print(resa);
		print(resb);
	}
    return 0;
}


inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

D. Minimum path 贪心,DP

给定n(2000),k(n^2),以及n*n的字母矩阵。
最多改变k个位置上的字母,求一条从左上到右下的字典序最小的路径。

考虑没有改变操作时,按距左上角的距离进行遍历,维护每个位置是否是“好的”。
首先原点(左上角)是好的。如果一个距原点d的位置是好的,需要满足两个条件:

  1. 左边或上边的位置是好的
  2. 在所有距原点d且满足第一个条件的位置中,这个位置的字符是最小的。
    终点(右下角)一定是好的,且一定至少有一条从原点到终点的全为好的路径,输出任意一条即可。

有改变操作时,就计算每个位置到左上角的最少的非a字符的数量(DP),把这个值不超过k的位置都改成a,再执行上述算法即可。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 2048, MOD = 1000000007;

char save[M][M];
int dp[M][M];
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	int n = read(), k = read();
	for(int i=1;i<=n;++i)
		scanf("%s",save[i]+1);
	memset(dp,0x3f,sizeof(dp));
	dp[0][1] = 0;
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=n;++j)
		{
			dp[i][j] = min(dp[i-1][j],dp[i][j-1])+(save[i][j]!='a');
			if(dp[i][j]<=k) save[i][j] = 'a';
		}
	}
	memset(dp,0,sizeof(dp));
	dp[0][1] = 1; //许可
	for(int dis=2;dis<=n<<1;++dis)
	{
		char mi = 'z';
		for(int r=1;r<=n;++r) if(dis-r<=n && dis-r>=1)
		{
			int c = dis-r;
			if(dp[r-1][c] || dp[r][c-1])
				mi = min(mi,save[r][c]);
		}
		for(int r=1;r<=n;++r) if(dis-r<=n && dis-r>=1)
		{
			int c = dis-r;
			if(dp[r-1][c] || dp[r][c-1])
				if(save[r][c]==mi)
					dp[r][c] = 1;
		}
	}
	stack<char> st;
	int r=n,c=n;
	while(1)
	{
		st.push(save[r][c]);
		if(r==1&&c==1) break;
		dp[r-1][c]?--r:--c;
	}
	while(!st.empty())
	{
		putchar(st.top());st.pop();
	}



    return 0;
}


inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/83280181