1. 程式人生 > 實用技巧 >Codeforces Round #639 Div2 A~D題解

Codeforces Round #639 Div2 A~D題解

閒話

又是隻有ABC的VP.D差一點就做完了.這個D主要就是想法稍微有點困難,情況沒考慮完全就裂開了.本身這個題算不上多難.由於這場最後unrated了,所以評分也不太準確.

A. Puzzle Pieces

題目大意:給你一個有三個凸口一個凹口的拼圖片,問你能不能拼出一個\(n*m\)的整塊的拼圖.

思路

顯然只有一行或者一列的時候必然可以,如果多擴充套件,只有\(2*2\)的可以做出來,其他的都不行.

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
		int a,b;scanf("%d%d",&a,&b);
		if(a == 1 || b == 1)	puts("YES");
		else if(a == 2 && b == 2)	puts("YES");
		else puts("NO");
    }
    return 0;
}

B. Card Constructions

原題大意:讓你用紙牌磊一個塔出來.塔的形式如下圖.受傷一開始有\(n\)個牌,每次都會擺出最大能擺出的塔,直到不能擺任何一個塔為止,問最多能擺出多少個塔.

思路

遞推式子很好想,先打個暴力看一下大概\(10^9\)有多少種塔.算出來也就兩萬多個,數量很小.顯然可以直接把所有的都打出來,算出要的數量,之後一直迭代二分出離\(n\)最近的塔的數量是多大,直到\(n\)沒有結果為止.順便記個數,答案就做完了.

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+7,M = 1e9 + 1e5;
vector<ll> cost;
void init()
{
	cost.push_back(2);
	ll cur = 5;
	while(cost.back() + cur <= M)
	{
		cost.push_back(cost.back() + cur);
		cur += 3;
	}
	// cout << cur << endl;
}
int main()
{
	init();
    int T;scanf("%d",&T);
    while(T--)
    {
		int n;scanf("%d",&n);
		vector<ll>::iterator iter;
		int res = 0;
		while((iter = upper_bound(cost.begin(),cost.end(),n)) != cost.begin())
		{
			--iter;
			// cout << (*iter) << endl;
			++res;
			n -= (*iter);
		}
		printf("%d\n",res);
    }
    return 0;
}

C. Hilbert's Hotel

原題大意:有無窮多個整數,且存在負數.給你\(n\)個數的數列\(a\)並且從\(0\)開始.對於每個整數\(k\),讓每個整數變成\(i+a_{i\%n}\).問是否存在兩個不同的整數\(i,j\)在這樣移動之後值相等了.

資料範圍:
\(1 \leq n \leq 2*10^5\)
\(-10^9 \leq a_i \leq 10^9\)

思路

顯然就是要求一個不定方程\(i + a_{i%n} = j +a_{j%n}\)是否存在一個整數解,並且兩邊不同.由於整個表示式裡存在一個取餘的式子,這很不牛逼,先把取餘的拆掉,變成一個牛逼的好做的形式.對於\(i\%n\)

\(i\%n=x\)進而可以把\(i\)表示成\(k_1*n+x\)其中\(k_1\)是一個整數,同理,也可以這樣把\(j\)替換掉,那麼之後可以得到一個形式牛逼的式子:\((k_1-k_2)*n=(a_y+y)-(a_x+x)\).顯然這個式子如果要成立,也就是要有整數解的話,因為\((k_1-k_2)\)肯定就是一個整數沒什麼,那麼就得滿足右邊的是整除\(n\)的才行,如果設\(c_x=x+a_x\)的話,那麼等式的判據實際上就是\(n|(c_y-c_x)\)也即\(c_y\equiv c_x(mod n)\).只要存在一組\(c_x\)\(c_y\)在模\(n\)的意義下同餘就說明存在兩個數最後會相等.
這個題分析到這裡,最後就寫個\(set\)儲存所有的餘數,看有沒有相同的就可以了.不過要防範負數取模,因為這裡\(a_i\)是存在負數的,因此要特殊處理.

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+7;
int a[N];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
		int n;scanf("%d",&n);
    	for(int i = 0;i < n;++i)	scanf("%d",&a[i]);
		set<int> tlb;
		int ok = 1;
		for(int i = 0;i < n;++i)
		{
			int b = ((a[i] + i) % n + n) % n;
			if(tlb.count(b))
			{
				ok = 0;
				break;
			}
			tlb.insert(b);
		}
		if(ok)	puts("YES");
		else puts("NO");
    }
    return 0;
}

D. Monopole Magnets
原題大意:太難翻了,去洛谷看吧連結

思路

假如這個圖是合法的,那麼答案應該就是整個圖裡黑色的聯通塊的數量,這個直接\(dfs\)就可以輕鬆解決了.難就難在這個題判斷無解比較麻煩.
首先比較容易看到的,第二個樣例給出的一種情況,即如果一行一列裡出現了兩個被隔開的黑色塊,那麼就無解,這是因為一行一列裡必須要有一個不動塊,那個不動塊在這一行或者列裡必須也有一個,可以發現不管怎麼放都必然使一邊的黑色塊被吸引到一個白色塊上(就是被夾著的白色塊部分).
還有一種,樣例沒有明顯的給出,可以手畫想到的:如果一行或列是全白的,那麼如果除此之外沒有一個全白的列或行就會無解.這個結論說的是:要麼存在全白的行和列,要麼都不存在.假如只存在一個,那麼必然會導致旁邊的有黑色塊的部分被吸引過去,因為那一行或列的純白區域也至少要放一個不動塊,這導致了矛盾.但是如果存在另外一個,情況就不一樣了,可以完全照著對角線的斜線方向放,既可以避免把黑色塊吸引過去,又可以保證每行每列至少一個的條件.至此,判斷無解的部分就完了.

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 1005;
char g[N][N];
const int dx[4] = {-1,0,1,0},dy[4] = {0,1,0,-1};
int allwr[N],allwc[N],exbr[N],exbc[N];
int n,m;
void dfs(int x,int y)
{
	g[x][y] = '$';
	for(int i = 0;i < 4;++i)
	{
		int a = x + dx[i],b = y + dy[i];
		if(a < 0 || a >= n || b < 0 || b >= m || g[a][b] != '#')	continue;
		dfs(a,b);
	}
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);
	cin >> n >> m;
	for(int i = 0;i < n;++i)	cin >> g[i];
    int ok = 1;
    //row
    for(int i = 0;i < n;++i)
    {
    	int cur = 0;
    	for(int j = 0;j < m;++j)
    	{
    		if(cur == 2 && g[i][j] == '#')
    		{
    			ok = 0;
    			break;
    		}
	  		if(g[i][j] != '#' && cur == 1)	cur = 2;
    		if(g[i][j] == '#' && cur == 0)	cur = 1;
    	}
    	if(!cur)	allwr[i] = 1;
    	else exbr[i] = 1;
    }
    //col
    for(int i = 0;i < m;++i)
    {
    	int cur = 0;
    	for(int j = 0;j < n;++j)
    	{
    		if(cur == 2 && g[j][i] == '#')
    		{
    			ok = 0;
    			break;
    		}
    		if(g[j][i] != '#' && cur == 1)	cur = 2;
    		if(g[j][i] == '#' && cur == 0)	cur = 1;
    	}
    	if(!cur)	allwc[i] = 1;
    	else exbc[i] = 1;
    }
	int exr = 0,exc = 0;
	for(int i = 0;i < n;++i)	
		if(allwr[i])
			exr = 1;
	for(int i = 0 ;i < m;++i)
		if(allwc[i])
			exc = 1;
	if(exr && !exc || exc && !exr)	ok = 0;
    if(!ok)	puts("-1");
    else
    {
    	int res = 0;
    	for(int i = 0;i < n;++i)
    		for(int j = 0;j < m;++j)
    			if(g[i][j] == '#')
    			{
    				++res;
    				dfs(i,j);
    			}
    	printf("%d\n",res);
    }
    return 0;
}