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\)
這個題分析到這裡,最後就寫個\(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;
}