CodeForces-808#D 题解

思路分析

分析样例 1:

3
1 3 2

原数组被分成 13 2 两部分,将 2 移到左边即可。

我们设左边部分的和为 \(s1\),右边为 \(s2\),可以发现对于任何分割方式,只有满足 \(s1 \pm x=s2 \mp x\) 才可以继续讨论答案是否成立。

推论 1:由于 \(x \in a\)\(a\) 为题目所给数列),因此 \(| \ s1-s2 \ |\equiv 0 \ (\bmod \ 2 \ )\)(换句话说,\(s1\)\(s2\) 的差值必然是偶数)。

推论 2:只有当 \(\dfrac{| \ s1-s2 \ |}{2} \in a\) 时,才能成立。

我们枚举 \(p \in \{0 \ , \ n+1\}\),得出区间 \(a_{[\ 1 \ ,\ p \ ]}\)\(a_{[\ p+1\ ,\ n\ ]}\) 之和,根据以上推论编写代码,可以用桶查询 \(\dfrac{| \ s1-s2 \ |}{2}\) 是否在原数列。

但真的如此吗?

考虑下面的样例:

5
1 2 2 5 4

这本来应该是 NO 的,但如果你只按照上述所说很有可能会输出 YES

按照上面所述,你的程序在将原数列分割成 1 2 25 4 时,\(\dfrac{| \ s1-s2 \ |}{2}\) 为 2,而 2 在桶中是可以被找到的,也就是说,你的程序认为从右边可以往左边移动一个 2,这样就成立了。

再仔细看看样例,2 分明在左边,右边是没有 2 的。

没错,你还要判断当应该从某一边移动数字时,你要判断该数字是否在这一边。

以上就是本道题的思路。如若存在常识性错误,请踢本蒟蒻。

代码实现

#define by_wanguan
#include<iostream>
#include<map>
#include<vector>
#define int long long
#define pb emplace_back
#define end cout<<"YES",exit(0)
using namespace std;
const int N=1e5+7;
int n,a[N],sum,p,s1,s2;
map<int,vector<int>> vis;
inline int abs(int &a,int &b){
  if(a>b) return a-b;
  else return b-a;
} 
signed main(){
  ios::sync_with_stdio(false),cin.tie(0);
  cin>>n;
  for(int i=1;i<=n;i++)
    cin>>a[i],sum+=a[i],vis[a[i]].pb(i);
    //记录a中a[i]出现的位置,方便后续判断abs(s2-s1)/2是否在区间内
  s2=sum,s1=0;
  for(p=0;p<=n+1;p++){//枚举左右区间分界线
    s1+=a[p],s2-=a[p];
    if(abs(s2-s1)%2!=0) continue;
    if(s1==s2) end;
    auto &pe=vis[abs(s2-s1)/2];
    if(s1<s2)
      {if(!pe.empty()&&pe.back()>p) end;}
      //由于上面的位置记录时单调的,只需要取最靠后面的位置就能判断右区间内是否有abs(s2-s1)/2
      //下面同理
    if(s1>s2)
      {if(!pe.empty()&&pe.front()<=p) end;}
  }
  cout<<"NO";
}

千万不要用 unordered_map,你会后悔的,警钟长鸣。

喵。

posted @ 2024-08-03 19:16  IOIAK_wanguan  阅读(26)  评论(0)    收藏  举报