Codeforces Round #696 (Div. 2) A~D題解
原題連結:Codeforces Round #696 (Div. 2)
A. Puzzle From the Future
顯然直接列舉每一位,能填1就填1.
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define forn(i,x,n) for(int i = x;i <= n;++i) const int N = 1e5+7; char s[N],ans[N]; int main() { int T;scanf("%d",&T); while(T--) { int n;scanf("%d",&n); scanf("%s",s + 1); int last = -1; forn(i,1,n) { int c = s[i] - '0'; if(c + 1 == last) ans[i] = '0',last = c; else ans[i] = '1',last = c + 1; } forn(i,1,n) printf("%c",ans[i]); puts(""); } return 0; }
B. Different Divisors
顯然取最近質數
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define forn(i,x,n) for(int i = x;i <= n;++i) const int N = 1e6+7; int primes[N],cnt; bool st[N]; void init(int N) { for(int i = 2;i <= N;++i) { if(!st[i]) primes[cnt++] = i; for(int j = 0;j < cnt && primes[j] * i <= N;++j) { st[primes[j] * i] = 1; if(i % primes[j] == 0) { break; } } } } int main() { init(N - 1); int T;scanf("%d",&T); while(T--) { ll d;scanf("%lld",&d); int cur = 1; forn(i,0,cnt - 1) { if(cur == 1) { if(primes[i] - cur >= d) cur = primes[i]; } else if(primes[i] - cur >= d) { printf("%lld\n",1ll*primes[i] * cur); break; } } } return 0; }
C. Array Destruction
題目大意:給定一個長度為\(2*n\)的陣列,再最開始,選定一個數\(x\),接下來執行若干步操作:選定陣列中兩個不同位置的元素,且和為\(x\).將兩者從陣列中刪除,並更新\(x\)為兩者中的較大者.問是否能刪除整個陣列,如果可以,輸出一組可行的方案,如果不能輸出無解.
思路
不妨把每個數對寫作\((x,y)\)並且保證\(x\geq y\).顯然可以注意到的是每一步操作之後,勢必會讓一開始選定的\(x\)的值變小,記作\(x_1\),那麼顯然\(x_1\)必須要是整個陣列的最大值,因為每次操作之後都會變小,變小之後不可能再消除最大值.
現在的問題是,已知\(x1\)
考慮如何得到\(y1\),一個錯誤的想法是先不管\(y1\)直接從\((x2,y2)\)開始構造,但是這樣會導致多解的存在,所以正確的方式是直接列舉\(y1\)的取值,之後整個局面就是固定的.整個列舉和\(check\)的過程佔到\(O(n^2)\)所以在每次尋找最大值和找最大值的時候使用其他資料結構查詢,例如\(map\),使查詢的複雜度降到\(O(logn)\)即可通過.
程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
typedef pair<int,int> pii;
#define x first
#define y second
const int N = 2005;
int a[N];
map<int,int> cnt;
inline void burn(int x)
{
if(--cnt[x] == 0) cnt.erase(x);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
cnt = map<int,int>();
int n;scanf("%d",&n);
forn(i,1,2 * n) scanf("%d",&a[i]);
sort(a + 1,a + 2 * n + 1);
int __r = 0;
for(int i = 1;i <= 2 * n - 1;++i)
{
cnt = map<int,int>();
forn(j,1,2 * n) cnt[a[j]]++;
int target = a[2 * n],y1 = a[i];
burn(a[2 * n]);burn(a[i]);
int del = 2,ok = 1;
vector<pii> op;
op.push_back({target,y1});
while(del < 2 * n)
{
auto _from = cnt.rbegin();
int x = (*_from).first;burn(x);
if(!cnt.count(target - x)) {ok = 0;break;}
op.push_back({x,target - x});
burn(target - x);
target = x;
del += 2;
}
if(!ok) continue;
printf("YES\n%d\n",op[0].x + op[0].y);
for(int i = 0;i < op.size();++i) printf("%d %d\n",op[i].x,op[i].y);
__r = 1;
break;
}
if(!__r) puts("NO");
}
return 0;
}
D. Cleaning
題目大意:有\(n\)堆石子,每堆石子各有\(a_i\)個,每次可以選取兩個相鄰的且都不空的石子堆裡同時拿走一個石子.如果有石子堆是空的,則相鄰關係不繼承.由於這個遊戲太不合理了,所以給你一次機會,在遊戲開始之前可以交換兩個相鄰的石子堆.問是否能移走所有的石子.
資料範圍:
\(2 \leq n \leq 2*10^5\)
\(1 \leq a_i \leq 10^9\)
思路
由於交換的條件比較吊比,先考慮一個子問題:沒有交換的時候如何判斷是否存在解.
值得注意的性質是:對於\(a_1\)來說如果想要消除這堆石子,那麼只能是讓\(a_1\)和\(a_2\)相鄰的去刪除,刪除之後會讓\(a_1=0,a_2-=a_1\).同樣的對最右端也是對稱的,不難想到可以先預處理出在不使用交換的前提下,從左邊往右邊一個一個推,和右邊往左邊分別推到\(i\)這個位置會導致\(i\)這個位置上有多少個石子,分別記做\(L_i,R_i\).那麼有了這個預處理之後,在沒有交換的前提下,我們可以把左邊的一段操作到\(i\)這個點和右邊一段操作到\(i\)個點這兩部分合起來,也就是如果從第一個元素一直往右推,從右往左一直推,假如說有解的話,那麼應該有一個分界點\(i\)使得他倆合併起來之後有什麼相等條件表示左右兩邊都可以刪除完,比較顯然就是\(R_i-L_{i-1}=0\).
考慮加入交換條件,因為交換隻限於在兩個相鄰的元素之間交換,所以可以列舉任意一對數\((i,i+1)\),那麼根據上面的分析,我們可以把左邊的部分直接合並地看做是一個元素\(L[i-1]\)右邊的元素合併地看做是\(R[i + 2]\),而中間兩個數進行交換,得到的一個等價的陣列\(t={L[i-1],a[i+1],a[i],R[i +2]}\).只需檢查這個陣列是否合法就可以了.
當然,這個做法無法處理邊角情況,也就是交換第一個元素和第二個元素,以及最後一個元素和倒數第二個元素.對於這兩種情況可以直接提前處理.但是這樣提前處理的時候又導致\(n=1\)的情況需要特判,繼續堆一下就行了.
程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
const int N = 2e5+7;
const ll INF = 1e18;
int a[N],b[N];
ll L[N],R[N];
bool check(vector<ll> a)
{
forn(i,1,a.size() - 1)
{
if(a[i] < a[i - 1]) return 0;
a[i] -= a[i - 1];
}
return a.back() == 0;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
vector<ll> a(n + 17,0),b(n + 17,0);
forn(i,1,n) scanf("%lld",&a[i]),b[i] = a[i];
if(n == 1)
{
puts("NO");
continue;
}
int ok = 0;
if(check(a)) ok = 1;
swap(a[1],a[2]); if(check(a)) ok = 1;swap(a[1],a[2]);
swap(a[n],a[n - 1]); if(check(a)) ok = 1;swap(a[n],a[n - 1]);
forn(i,1,n) L[i] = R[i] = -INF;
forn(i,1,n) if(b[i] < b[i - 1]) break;else L[i] = b[i] -= b[i - 1];
forn(i,1,n) b[i] = a[i];
forr(i,1,n) if(b[i] < b[i + 1]) break;else R[i] = b[i] -= b[i + 1];
forn(i,2,n - 2)
{
vector<ll> t = {L[i - 1],a[i + 1],a[i],R[i + 2]};
if(L[i - 1] == -INF || R[i + 2] == -INF) continue;
if(check(t)) {ok = 1;break;}
}
if(ok) puts("YES");
else puts("NO");
}
return 0;
}