Codeforces Round #682 (Div. 2)

Codeforces Round #682 (Div. 2)

传送门(点击传送

A. Specific Tastes of Andre

题意:
  定义如果一个数组如果这个数组的所有元素之和可以整除这个数组的元素个数,那么称这个数组为好数组,现在给出数组元素个数 n ,请构造这个数组,每个元素不小于 1 且不大于 100。( t 组数据)

思路:
  直接构造所有元素都是 1 即可。

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t,n;
    cin>>t;
    while(t--){
    
    
        cin>>n;
        for(int i=1;i<=n;i++){
    
    
            cout<<1<<" ";
        }
        cout<<endl;
    }
    return 0;
}

B. Valerii Against Everyone

题意:
  现在给出一个 n 个元素的序列 b,设每个元素为 b i b_i bi,并且有对应的序列 a ,有对应关系 a i = 2 b i a_i=2^{b_i} ai=2bi,现在问是否有符合条件的区间 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] [ l 2 , r 2 ] [l_2,r_2] [l2,r2],满足在 a 上的区间和相等,且有 l 1 ⩽ r 1 < l 2 ⩽ r 2 l_1\leqslant r_1< l_2\leqslant r_2 l1r1<l2r2。( t 组数据)

思路:
  对于 2 X 1 + 2 X 2 + . . . + 2 X k 2^{X_1}+2^{X_2}+...+2^{X_k} 2X1+2X2+...+2Xk 2 Y 1 + 2 Y 2 + . . . + 2 Y l 2^{Y_1}+2^{Y_2}+...+2^{Y_l} 2Y1+2Y2+...+2Yl ,若 X 1 , X 2 , . . . X k X_1,X_2,...X_k X1,X2,...Xk两两不等且 Y 1 , Y 2 , . . . Y l Y_1,Y_2,...Y_l Y1,Y2,...Yl两两不等,那么两个式子必然不等。对于 2 的幂次方,一定有两个相同的元素才能构成等式成立。然而只要有两个相同的元素,那么就必定存在两个区间使得区间和相同(这时区间长度就为1,连个区间分别是这个相等的元素)。所以本体思路即找序列中是否有相等元素即可。

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t,n,get_num;
    cin>>t;
    while(t--){
    
    
        cin>>n;
        bool flag=false;
        unordered_set<int>unset;
        for(int i=1;i<=n;i++){
    
    
            cin>>get_num;
            if(unset.find(get_num)!=unset.end()) flag=true;
            unset.insert(get_num);
        }
        if(flag) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

C. Engineer Artem

题意:
  有一个 n 排 m 列的矩阵 a ,给出矩阵每个元素 a i , j a_{i,j} ai,j,现在让构造矩阵 b 。且 b i , j b_{i,j} bi,j可以取 a i , j a_{i,j} ai,j,也可以取 a i , j + 1 a_{i,j}+1 ai,j+1,请构造出矩阵 b 使得矩阵 b 相邻的元素都不相等。

思路:
  构造题,思路为把矩阵看作国际象棋的棋盘,保证所有黑色格子的元素为奇数,所有白色格子的元素为偶数即可。

代码:

#include<bits/stdc++.h>
using namespace std;
int num[105][105];
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t,n,m;
    cin>>t;
    while(t--){
    
    
        cin>>n>>m;
        for(int i=1;i<=n;i++){
    
    
            for(int j=1;j<=m;j++){
    
    
                cin>>num[i][j];
                if((i+j)%2){
    
    
                    if(num[i][j]%2==0) num[i][j]++;
                }else{
    
    
                    if(num[i][j]%2==1) num[i][j]++;
                }
            }
        }
        for(int i=1;i<=n;i++){
    
    
            for(int j=1;j<=m;j++){
    
    
                cout<<num[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    return 0;
}

D. Powerful Ksenia

题意:
  有一个 n 个元素的序列 a ,现在你可以进行如下操作,每次操作可以选 i , j , k i,j,k i,j,k 并让 a i , a j , a k a_i,a_j,a_k ai,aj,ak都等于 a i ⨁ a j ⨁ a k a_i\bigoplus a_j\bigoplus a_k aiajak ⨁ \bigoplus 为按位异或),问能否进行至多 n 次操作使得序列的所有元素都相等。

思路:
  首先,我们有以下式子 x ⨁ y ⨁ y = x x\bigoplus y\bigoplus y=x xyy=x ,我们可以让3个元素中相等的两个元素变为另一个元素。所以对于元素个数为奇数个的序列一定是可以的,因为我们可以通过操作1、2、3, 1、4、5,… ,1、n-1、n 这种操作让2和3相等,4和5相等,… ,n-1和n相等。之后再进行一次1、2、3, 1、4、5,… ,1、n-1、n的操作就可以让所有元素都跟元素位置为1的元素相等,总共n-1次操作。对于元素个数为偶数个的序列,我们这么操作就需要最后多余出的一个元素等于我们前面这种操作下来的值,我们操作过后有 a 1 , a 1 , a 1 , . . . a 1 , a n a_1,a_1,a_1,...a_1,a_n a1,a1,a1,...a1,an我们要让这个 a n a_n an刚好等于我们的 a 1 a_1 a1,那么如何判断呢,我们之前是 a 1 ⨁ a 2 ⨁ a 3 a_1\bigoplus a_2\bigoplus a_3 a1a2a3,再用产生的结果 a 1 a_1 a1去进行 a 1 ⨁ a 4 ⨁ a 5 a_1\bigoplus a_4\bigoplus a_5 a1a4a5,相当于初始序列的 a 1 ⨁ a 2 ⨁ a 3 ⨁ a 4 ⨁ a 5 a_1\bigoplus a_2\bigoplus a_3\bigoplus a_4\bigoplus a_5 a1a2a3a4a5这么一个累积异或,所以我们进行到最后的 a 1 ⨁ a n − 1 ⨁ a n a_1\bigoplus a_{n-1}\bigoplus a_n a1an1an就相当于从 a 1 a_1 a1一直异或到 a n − 1 a_{n-1} an1所以我们只需要判断从从 a 1 a_1 a1一直异或到 a n − 1 a_{n-1} an1的结果知否等于 a n a_n an即可,即( a 1 ⨁ a 2 ⨁ a 3 ⨁ . . . ⨁ a n a_1\bigoplus a_2\bigoplus a_3\bigoplus ...\bigoplus a_n a1a2a3...an是否为 0 )。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int num[N];
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
    
    
        cin>>num[i];
    }
    if(n%2){
    
    
        cout<<"YES"<<endl<<n-1<<endl;
        for(int i=2;i<=n;i+=2){
    
    
            cout<<"1 "<<i<<" "<<i+1<<endl;
        }
        for(int i=2;i<=n;i+=2){
    
    
            cout<<"1 "<<i<<" "<<i+1<<endl;
        }
    }else{
    
    
        int xor_n=0;
        for(int i=1;i<=n;i++) xor_n^=num[i];
        if(xor_n==0){
    
    
            cout<<"YES"<<endl<<n-2<<endl;
            for(int i=2;i<n;i+=2){
    
    
                cout<<"1 "<<i<<" "<<i+1<<endl;
            }
            for(int i=2;i<n;i+=2){
    
    
                cout<<"1 "<<i<<" "<<i+1<<endl;
            }
        }else{
    
    
            cout<<"NO"<<endl;
        }
    }
    return 0;
}

E. Yurii Can Do Everything

题意:
  有一个序列 a ,问有多少个区间 [ i , j ] [i,j] [i,j] 满足 a i ⨁ a j = a i + 1 + a i + 2 + . . . + a j − 2 + a j − 1 a_i\bigoplus a_j=a_{i+1}+a_{i+2}+...+a_{j-2}+a_{j-1} aiaj=ai+1+ai+2+...+aj2+aj1
  
思路:
  假设我们 m a x ( a i , a j ) max(a_i,a_j) max(ai,aj)的二进制最高位为 k ,那么我们就一定有 a i + 1 + a i + 2 + . . . + a j − 2 + a j − 1 < 2 k + 1 a_{i+1}+a_{i+2}+...+a_{j-2}+a_{j-1}<2^{k+1} ai+1+ai+2+...+aj2+aj1<2k+1(因为 a i a_i ai a j a_j aj的异或值不会超过 m a x ( a i , a j ) max(a_i,a_j) max(ai,aj)的二进制位数)。所以 ①我们先假设 a i > a j a_i>a_j ai>aj,算得 a i a_i ai的二进制最高位为 k ,然后从左向右枚举 i ,对于每一个 i ,都去向右一边枚举 j 一边判断是否符合题意直到 2 k + 1 < a i + 1 + a i + 2 + . . . + a j − 2 + a j − 1 2^{k+1}<a_{i+1}+a_{i+2}+...+a_{j-2}+a_{j-1} 2k+1<ai+1+ai+2+...+aj2+aj1为止。之后 ②我们再假设 a i < a j a_i<a_j ai<aj算得 a j a_j aj的二进制最高位为k,然后从右向左枚举 j ,对于每一个 j ,都去向左一遍枚举 i 一边判断是否符合题意直到 2 k + 1 < a i + 1 + a i + 2 + . . . + a j − 2 + a j − 1 2^{k+1}<a_{i+1}+a_{i+2}+...+a_{j-2}+a_{j-1} 2k+1<ai+1+ai+2+...+aj2+aj1为止。①②两次计算可能有重复值,去重即可。
  接下来证明一下为什么这么看似暴力的做法时间不会 t ,首先你可能会觉得这样暴力不是 n 2 n^2 n2的复杂度吗,那么让我们看一下 n 2 n^2 n2复杂度需要满足什么条件。 2 A 1 > 2 k + 1 > A 2 + A 3 + . . . + A n 2A_1>2^{k+1}>A_2+A_3+...+A_n 2A1>2k+1>A2+A3+...+An后面我们都会把 2 k + 1 2^{k+1} 2k+1省略掉,有 2 A 2 > A 3 + A 4 + . . . + A n 2A_2>A_3+A_4+...+A_n 2A2>A3+A4+...+An 2 A 3 > A 4 + A 5 + . . . + A n 2A_3>A_4+A_5+...+A_n 2A3>A4+A5+...+An,…, 2 A n − 1 > A n 2A_{n-1}>A_n 2An1>An,下面我们反着推回去。
   2 A n − 1 > A n ⇒ A n − 1 > 1 2 A n 2A_{n-1}>A_n\Rightarrow A_{n-1} > \frac{1}{2}A_n 2An1>AnAn1>21An
   2 A n − 2 > A n − 1 + A n > 1 2 A n + A n ⇒ A n − 2 > 3 4 A n 2A_{n-2}>A_{n-1}+A_{n}>\frac{1}{2}A_n+A_n\Rightarrow A_{n-2}>\frac{3}{4}A_n 2An2>An1+An>21An+AnAn2>43An
   2 A n − 3 > A n − 2 + A n − 1 + A n > 3 4 A n + 1 2 A n + A n ⇒ A n − 3 > 9 8 A n 2A_{n-3}>A_{n-2}+A_{n-1}+A_n>\frac{3}{4}A_n+\frac{1}{2}A_n+A_n\Rightarrow A_{n-3}>\frac{9}{8}A_n 2An3>An2+An1+An>43An+21An+AnAn3>89An
  …
   A 1 > 1 2 ∗ ( 3 2 ) n − 2 A n A_1>\frac{1}{2}*(\frac{3}{2})^{n-2}A_n A1>21(23)n2An
  又因为 A 1 < 2 30 A_1<2^{30} A1<230,假设 A n A_n An为1的话实际也不会有多少项,所以对于每一个外层循环的位置 i 或 j ,内循环的枚举是log级的时间复杂度。所以我们整体的时间复杂度为 O ( n ∗ l o g a i ) O(n*log{a_i}) O(nlogai)
  
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int num[N];
ll sum[N];
int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    unordered_set<ll>unset;
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
    
    
        cin>>num[i];
        sum[i]=sum[i-1]+1LL*num[i];
    }
    for(int i=1;i<=n;i++){
    
    
        int limit=num[i],k=0;
        while(limit){
    
    
            k++;
            limit>>=1;
        }
        limit=1<<k;
        for(int j=i+2;j<=n;j++){
    
    
            int x=num[i]^num[j];
            if(sum[j-1]-sum[i]==(ll)x){
    
    
                ll sign=1LL*i*1000000+1LL*j;
                unset.insert(sign);
            }
            if(sum[j-1]-sum[i]>=limit) break;
        }
    }
    for(int j=n;j>=1;j--){
    
    
        int limit=num[j],k=0;
        while(limit){
    
    
            k++;
            limit>>=1;
        }
        limit=1<<k;
        for(int i=j-2;i>=1;i--){
    
    
            int x=num[i]^num[j];
            if(sum[j-1]-sum[i]==(ll)x){
    
    
                ll sign=1LL*i*1000000+1LL*j;
                unset.insert(sign);
            }
            if(sum[j-1]-sum[i]>=limit) break;
        }
    }
    cout<<unset.size()<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/blaction/article/details/109687553