返回顶部

Codeforces Round #696 (Div. 2) D. Cleaning (思维,前缀和)

  • 题意:有一堆石子,你每次可以选择相邻(就算两堆石子中间有很多空堆也不算)的两堆石子,使得两堆石子的个数同时\(-1\),你在刚开始的时候有一次交换相邻石子的机会,问你最后能否拿走所有石子.
  • 题解:对于第一堆石子和最后一堆石子,它们只能靠第二堆石子和倒数第二堆石子减去才合法,所以我们由第一堆石子不断向右推和最后一堆石子不断向左推,这个过程可以用前缀和\(pre\)与后缀和\(suf\)表示.如果我们当前选择堆\(<i-1,i>\)的话,那么前缀和\(pre[i-2]\)和后缀和\(suf[i+1]\)是不会受到任何影响的,如果问题出在我们当前选择的这个堆上的话,我们就必须保证\(pre[i-2]\)\(suf[i+1]\)都是合法的,同时,我们将\(pre[i-2]\)\(a[i-1]\)合并后和\(suf[i+1]\)\(a[i]\)合并后的值应该是合法且相等的.于是乎,我们可以枚举所有堆,然后判断不反转和反转是否合法.这里注意,如果\(pre[i]\)不合法,我们将\(pre[i]=-1\),那么\(i\)后面的\(pre\)也都是不合法的,都标记为\(-1\),\(suf\)同理.
  • 代码:
#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define me memset
#define rep(a,b,c) for(int a=b;a<=c;++a)
#define per(a,b,c) for(int a=b;a>=c;--a)
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
using namespace std;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}

int _;
int a[N];
int pre[N],suf[N];

bool check(int i){
	if(pre[i-2]!=-1 && suf[i+1]!=-1 && a[i-1]>=pre[i-2] && a[i]>=suf[i+1] && a[i-1]-pre[i-2]==a[i]-suf[i+1])
		return true;
	return false;
}

int main() {
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

	cin>>_;
	while(_--){
		int n;
		cin>>n;
		rep(i,1,n) cin>>a[i];

		pre[0]=0,suf[n+1]=0;

		rep(i,1,n){
			if(pre[i-1]==-1 || a[i]<pre[i-1]){
				pre[i]=-1;
			}else{
				pre[i]=a[i]-pre[i-1];
			}
		}

		per(i,n,1){
			if(suf[i+1]==-1 || a[i]<suf[i+1]){
				suf[i]=-1;
			}else{
				suf[i]=a[i]-suf[i+1];
			}
		}

		bool flag=false;

		rep(i,2,n){
			if(check(i)) {flag=true;break;}
			swap(a[i],a[i-1]);
			if(check(i)) {flag=true;break;}
			swap(a[i],a[i-1]);
			if(pre[i-1]==-1) break; //小小的优化
		}

		if(flag) cout<<"YES\n";
		else cout<<"NO\n";
	}

    return 0;
}
posted @ 2021-01-29 01:35  Rayotaku  阅读(39)  评论(0编辑  收藏  举报