Codeforces Round 882 (Div. 2)

A. The Man who became a God

题意:输入给出包含n个数的数组,和要分成的区间个数k,定义区间权值为所有相邻两个数的差值的和,要求将数组分为k个子区间使得所有子区间的权值和最小

思路:将一个权值为x的区间分为两个子区间,假设两个子区间相邻处的两个元素的差值为y,两个子区间的权值分别为a和b,则有x=a+b+y,则若想要使a+b最小,就要使y最大,所以本题只需要将所有的相邻元素差求出来,然后按照从大到小的顺序找到前n-k个差,这n-k个差所在的位置就是要将区间分开的边界

ac代码:

#include
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair pii;
const int N=1100;
int n,k;
int b[N];//标记断点
int a[N];
void solve()
{
    vector g;
    cin>>n>>k;
    memset(b,0,sizeof b);
    for(int i=0;i>a[i];
        if(i)g.pb({abs(a[i]-a[i-1]),i});
    }
    if(n==k)
    {
        cout<<0<=g.size()-k+1;i--)
    {
        b[g[i].second]=1;//标记断点
    }
    // for(int i=0;i>T;
    while(T--)
    {
        solve();
    }
}

B. Hamon Odyssey

题意:给出包含n个数的数组,要求将该数组划分为数个子区间,要求将所有区间进行与运算之后求和,使得和最小并且区间数最多

trick:与运算:0&0=0,1&0=0,0&1=0,1&1=1,由此可以发现,与运算存在性质:0在与运算之后不可能变为1,而1与运算之后可能变为0,所以对一个数进行与运算,只会越运算越小或不变,而不会变大。

思路:本题是在要求与和最小的前提下使区间数最多,所以首先要考虑这个前提,既然与运算只会越运算越小,所以对于本题,考虑两种情况:1、f(1,n)!=0,既然所有数全部进行与运算之后结果仍然不为零,则将整段区间分为任意情况的子区间,可知每个子区间的与都会大于f(1,n),自然与的和也会大于f(1,n),所以对于f(1,n)!=0的情况,结果为1。2、f(1,n)=0,既然整段区间的与已经达到了最小,则此时再考虑怎么分才能使得区间数最多,既然题目要求的是子区间而不是子序列,所以只需要从前往后遍历一遍,找出所有与为1的子区间即可,特殊处理如果最后剩下的区间与不为0,则将该区间与前面的一个与为0的区间合并为一个区间即可,因为0与任何数都为0

ac代码:

#include
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair pii;
const int N=2e5+10;
int n;
int a[N];
void solve()
{
    cin>>n;
    int now;
    for(int i=0;i>a[i];
        if(!i)now=a[i];
        else now&=a[i];
    }
    if(now)cout<<1<>T;
    while(T--)
    {
        solve();
    }
}

C. Vampiric Powers, anyone?

题意:输入给出n个非负整数,下标1~n,可以进行任意次以下操作:在数组末尾添加一个原数组的任意后缀异或和,要求输出可以得到的最大后缀异或和

trick:异或是一个可逆运算,如果a^b=c,则c^b=a,也就是一个数两次异或同一个数,就相当于没有异或这个数,所以可以通过前缀异或和来求区间异或和

思路:本题可以通过两次后缀异或和操作来实现区间异或和操作,若要得到xor[l-r],可以通过a[n+1]=xor[r+1,n],则a[n+2]=xor[l,n+1]=xor[l-r],则该题就转化成了求原数组的最大子区间异或和,而本题数组中的数最大不超过256,也就是说任意子区间的异或和都不会超过256,我们可以用一个set来存已经出现过的前缀异或和,不同前缀区间有相同的异或和可以只做一次运算,因为本题不需要找出具有最大异或和的区间是哪一段

ac代码:

#include
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair pii;
const int N=1e5+10;
int a[N];
int n;
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    set s;
    int ans=0;
    s.insert(0);
    for(int i=1;i<=n;i++)
    {
        a[i]^=a[i-1];
        for(auto j:s)
        {
            ans=max(ans,a[i]^j);
            //将此前缀异或和与所有之前出现过的前缀异或和进行异或
            //可以得到以当前点之前的所有点为起点,以当前点为终点的区间的异或和
            //而我们只需要得到最大异或和,而这些前缀异或和中又有很多重复数字
            //所以用set来保存出现过的前缀异或和进行异或即可
        }
        s.insert(a[i]);
    }
    cout<>T;
    while(T--)
    {
        solve();
    }
}

你可能感兴趣的:(Codeforces,算法,数据结构)