Codeforces Round #717 (Div. 2)(ABC)

A . Tit for Tat

传送门

题目大意:

给n个非负数,和最多k次修改,每次修改可以将不同下标的元素分别+1和-1,求修改后最小字典序的结果

思路:

因为就100个不超过的数,暴力,从左到右,每个元素减到0即可,同时k要大于0,while( a[i]>0 && k) a[i]--,k--;即可,不用想太多= = 

B . AGAGA XOOORRR

传送门

题目大意:给n个非负数,可以将相邻的两个元素进行异或操作,然后用异或结果代替两个元素放入原位,最后剩余所有元素相等,且至少剩两个元素,。问这样的结果是否存在

思路:思维,异或前缀和

异或性质:交换律,结合律,前缀和,并且区间异或和只要两个端点异或就相当于“减”了

进入正题,举两个例子最后成立的情况有两种,假设最后全剩余5,如果剩余偶数的5,可以归并成(5,5),可以发现此时所有元素异或和为0,因为相等元素异或为0

如果剩余奇数的5,可以归并成(5,5 , 5),但是存在(5)的情况,(5,5 , 5)可以推到(5),但是反过来不行,可以发现最后异或的结果是剩余的元素,只要证明存在3个相同元素即可,第一个5异或和5,前两个5异或和为0,全部异或和为5,假设剩余元素为a,所以我们只要找到异或前缀和为a,0,a的情况即可,即在前n-1个前缀和里找a,0的情况就好了。

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(x,y) memset(x,y,sizeof(x))
#define all(v) v.begin() , v.end()
#define pb(v) push_back(v)
#define INF 0x3f3f3f3f
#define int long long
#define lson p*2,l,mid
#define rson p*2+1,mid+1,r
typedef long long ll;
const int N =1e6+10;
const int mod = 1e9+7;
int a[N];
int n,k;
int sum[2010];//异或前缀和
bool solve()
{
    int flag=0;
    if( !sum[n] ) return true;//异或和为0,返回真
    //处理最后剩余三个元素的情况
    _for(i,1,n-1)
    {
        if( sum[i]==0 )
        {
            flag=i;//找到前两个异或和为0的点
        }
    }
    int f=0;
    //从前flag数中,找异或前缀和等于sum[n]的情况
    _for(i,1,flag)
    {
        if( sum[i] == sum[n]) f=1;
    }
    if(f) return true;
    return false;
}
signed main()
{
//    freopen("data.txt","r",stdin);
    IOS;
    int T;
    cin>>T;
    while( T-- )
    {
        mst(sum,0);

        cin>>n;
        _for(i,1,n)
        {
             cin>>a[i];
             sum[i] = sum[i-1] ^ a[i];//求异或前缀和
        }
        if( solve() ) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

C . Baby Ehab Partitions Again

传送门

题目大意:

一个序列包含n个非负数,问是否可以将其分任意为两段而不存在两段数值和相等的情况,可以删除一个数,问最小删的数是多少,不用删就输出0

思路:背包 / 暴力

第一眼暴力 ,但是写完线段树板子只剩10min了,直接放弃了QAQ,debug还要30min以上= =

正解,背包做法(没看出来T - T

裸的背包,设所有数总和为sum,dp[i]为容量为i的背包最多存的数值,最后看是否2 * dp[sum/2] == sum ?即可

删数可以证明删最小奇数。

假如存在两段数值和相等的情况,那么sum肯定为偶数,即去奇数后便无法划分为两段和的序列了。

进一步考虑,如果所有元素都为偶数,怎么删呢

将所有元素除以2,再删最小奇数,除以2后是等价的

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <map>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(x,y) memset(x,y,sizeof(x))
#define all(v) v.begin() , v.end()
#define pb(v) push_back(v)
#define INF 0x3f3f3f3f
#define lson p*2,l,mid
#define rson p*2+1,mid+1,r
typedef long long ll;
const int N =1e5+10;
int n;
int sum;
int a[N];
int dp[200005];
int flag;//是否全偶
int oddmin =INF;//最小奇数
int temp;
signed main()
{
    //!!!
//    freopen("data.txt","r",stdin);
    //!!!
    IOS;
    cin>>n;
    _for(i,1,n)
    {
        cin>>a[i];
        sum += a[i];
        if( a[i] & 1)
        {
            if( a[i] < oddmin)
            {
                oddmin = a[i];
                temp = i;
            }
        }
    }
    //背包
    _for(i,1,n)
    {
        for(int j=sum/2 ;j>=a[i] ;j--)
        {
            dp[j] = max(dp[j] , dp[j-a[i]] + a[i]);
        }
    }
    
    if( sum - dp[sum/2] * 2) cout<<0<<endl;
    else
    {
        if( oddmin < INF) cout<<1<<endl<<temp<<endl;
        else//全偶的情况
        {
            int cnt=0;
            while( oddmin>=INF )
            {
                for(int i=1 ;i<=n ;i++)
                {
                    a[i] /=2;
                    if( a[i] & 1)
                    if( a[i] < oddmin)
                    {
                        oddmin = a[i];
                        temp = i;
                    }
                }
                cnt++;
            }
            cout<<1<<endl<<temp<<endl;
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值