CF1478C. Nezzar and Symmetric Array(伪)题解

只过了Pretest,可能有误,欢迎批评指正

题面:https://codeforces.com/contest/1478/problem/C#

题意:

a数组有2n个元素,两两不同,且每个数和它的绝对值同时出现。如[-3,3,-1,1]。

根据公式 d i = ∑ j = 1 2 n ∣ a i − a j ∣ d_{i}=\sum_{j=1}^{2 n}\left|a_{i}-a_{j}\right| di=j=12naiaj 求得d数组。即:d数组中每个元素,为a数组相应元素和其他所有元素绝对值的和。

已知d数组,求是否存在合法的a数组。

思路:

显然,d数组和a数组中,元素的顺序没什么卵用

假设a数组从大到小排好了序,只考虑a数组中的正数(也就是前n个)

容易发现

d i = ∑ j = 1 n m a x ( 2 a i , 2 a j ) d_i= \sum_{j=1}^{n} max(2a_i,2a_j) di=j=1nmax(2ai,2aj)

看图!

举个例子

例如a=[6,3,1,-1,-3,-6]

d 1 = 2 ∗ 6 + 2 ∗ 6 + 2 ∗ 6 = 36 d_1=2*6+2*6+2*6=36 d1=26+26+26=36,因为 m a x ( 6 , 6 ) = 6 , m a x ( 6 , 3 ) = 6 , m a x ( 6 , 1 ) = 6 max(6,6)=6, max(6,3)=6, max(6,1)=6 max(6,6)=6,max(6,3)=6,max(6,1)=6

d 2 = 2 ∗ 6 + 2 ∗ 3 + 2 ∗ 3 = 24 d_2=2*6+2*3+2*3=24 d2=26+23+23=24,因为 m a x ( 3 , 6 ) = 6 , m a x ( 3 , 3 ) = 3 , m a x ( 3 , 1 ) = 3 max(3,6)=6, max(3,3)=3, max(3,1)=3 max(3,6)=6,max(3,3)=3,max(3,1)=3

d 3 = 2 ∗ 6 + 2 ∗ 3 + 2 ∗ 1 = 20 d_3=2*6+2*3+2*1=20 d3=26+23+21=20,因为 m a x ( 1 , 6 ) = 6 , m a x ( 1 , 3 ) = 3 , m a x ( 1 , 1 ) = 1 max(1,6)=6, max(1,3)=3, max(1,1)=1 max(1,6)=6,max(1,3)=3,max(1,1)=1

所以d=[36,24,20,20,24,36]

由于顺序没卵用,因此,若d数组是这一坨数,不管顺序如何,都一定YES

d有什么性质呢?

假设a数组从大到小排列,假设a数组从大到小排列,假设a数组从大到小排列,重要的事情说三遍!后文默认a是从大到小排好序的。d数组根据公式推出:

首先, d 1 = 2 n a 1 d_1=2na_1 d1=2na1,因为 a 1 a_1 a1一定是a数组中最大的数,根据上述公式, d 1 = 2 a 1 + 2 a 1 + ⋯ + 2 a 1 = 2 n a 1 d_1=2a_1+2a_1+\cdots+2a_1=2na_1 d1=2a1+2a1++2a1=2na1

只有 a 1 > a 2 a_1>a_2 a1>a2,而且 a 3 ⋯ a n a_3\cdots a_n a3an均小于 a 2 a_2 a2。因此 d 2 = 2 a 1 + 2 a 2 + 2 a 2 + ⋯ + 2 a 2 = 2 a 1 + 2 ( n − 1 ) a 2 d_2=2a_1+2a_2+2a_2+\cdots+2a_2=2a_1+2(n-1)a_2 d2=2a1+2a2+2a2++2a2=2a1+2(n1)a2

同理可得 d 3 = 2 a 1 + 2 a 2 + 2 ( n − 2 ) a 3 d_3=2a_1+2a_2+2(n-2)a_3 d3=2a1+2a2+2(n2)a3

d k = 2 a 1 + 2 a 2 + ⋯ + 2 a k − 1 + 2 ( n − k + 1 ) a k d_k=2a_1+2a_2+ \cdots+2a_{k-1}+2(n-k+1)a_k dk=2a1+2a2++2ak1+2(nk+1)ak

对于a的负数部分,其对应的d和正数部分对称出现。即 a 1 = − a 2 n , d 1 = d 2 n ; a 2 = − a 2 n − 1 , d 2 = d 2 n − 1 a_1=-a_{2n}, d_1=d_{2n}; a_2=-a_{2n-1}, d_2=d_{2n-1} a1=a2n,d1=d2n;a2=a2n1,d2=d2n1。也就是说,d数组中的数必须成对出现。由于上面的公式每一项都乘了2,因此d数组中的数必须都是偶数

这一顿操作猛如虎,有什么用呢?别急!马上出来了!

那么怎么样的一坨数才算YES呢?

首先,都是偶数,可以在输入时就进行判断。

其次,必须成对出现,从大到小排序后,必须满足“对于任意的奇数i( 1 ≤ i ≤ 2 n − 1 1\leq i\leq 2n-1 1i2n1),有 d [ i ] = = d [ i + 1 ] d[i]==d[i+1] d[i]==d[i+1]。判断的过程可以同时去重,以便和上述的a数组对应,如d1对应a1。

然后!!!!!

假设d数组已经从大到小排列并去重(相同的两个数只保留一个)

由于 d 1 = 2 n a 1 d_1=2na_1 d1=2na1,所以必须有 d 1 % 2 ( n − 1 ) = = 0 d_1\%2(n-1)==0 d1%2(n1)==0 a 1 = d 1 / 2 n a_1=d_1/2n a1=d1/2n

由于 d 2 = 2 a 1 + 2 ( n − 1 ) a 2 d_2=2a_1+2(n-1)a_2 d2=2a1+2(n1)a2,所以必须有 ( d 2 − 2 a 1 ) % 2 ( n − 1 ) = = 0 (d_2-2a_1)\%2(n-1)==0 (d22a1)%2(n1)==0 a 2 = ( d 2 − 2 a 1 ) / 2 ( n − 1 ) a_2=(d_2-2a_1)/2(n-1) a2=(d22a1)/2(n1)

由于 d 3 = 2 a 1 + 2 a 2 + 2 ( n − 2 ) a 3 d_3=2a_1+2a_2+2(n-2)a_3 d3=2a1+2a2+2(n2)a3,所以必须有 ( d 3 − 2 a 1 − 2 a 2 ) % 2 ( n − 2 ) = = 0 (d_3-2a_1-2a_2)\%2(n-2)==0 (d32a12a2)%2(n2)==0

对于任意的 2 ≤ k ≤ n 2\leq k\leq n 2kn,有 ( d k − 2 a 1 − 2 a 2 − ⋯ − 2 a k − 1 ) % 2 ( n − k + 1 ) = = 0 (d_k-2a_1-2a_2-\cdots-2a_{k-1})\%2(n-k+1)==0 (dk2a12a22ak1)%2(nk+1)==0

每次“减去的数”多了一个 2 a i 2a_i 2ai,因此可以用一个变量维护。如果其中任何一个不为0,则输出NO

还有!!!!!

我们假设a数组是单调递减且不重复的,前n个数是正数!!!!如果算到哪一步, d k − 2 a 1 − 2 a 2 − ⋯ 2 a k − 1 ≤ 0 d_k-2a_1-2a_2-\cdots2a_{k-1}\leq0 dk2a12a22ak10了,那就没了!!!!如果算出哪一步的 a i a_i ai大于上一步的 a i − 1 a_{i-1} ai1,也不可以!!!!!

C++代码

#include 
#include 
using namespace std;
#define ll long long
ll d[200005], n;
int main()
{
     
    ios::sync_with_stdio(false);
    int t; cin>>t;
    while(t--)
    {
     
        cin>>n;
        for(ll i=1;i<=n*2;i++)
            cin>>d[i]; 
        sort(d+1,d+2*n+1,[](ll x,ll y)->bool{
     return x>y;}); //从大到小排序

        bool flag = true;
        for(ll i=1;i<=n*2-1;i+=2) //我没有去重!!!只判断了
        {
     
            if(d[i]%2 || d[i]!=d[i+1]) flag = false; //必须两两一组出现,而且得是偶数
        }
        if(d[1]%(n*2)!=0) flag = false;

        ll pre = d[1]/n, cnt = 2*n, last = d[1]/n; //已经加的,剩余个数,上一个a[i]的二倍
        for(ll i=3;i<=2*n-1 && flag;i+=2)
        {
     
            cnt-=2; //剩余数字个数-2
            d[i] -= pre;
            //cout<
            flag &= (d[i]>0);
            flag &= (d[i]%cnt==0);
            pre += d[i]/cnt*2; //已经求过的数之和
            ll now = d[i]/cnt*2; //这一个a[i]的二倍
            flag &= (now<last); //a数组必须严格单调递减
            //cout<
            last = now;
        }
        if(flag) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

你可能感兴趣的:(算法)