Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)

比赛链接:Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)

A. Even Subset Sum Problem

题意:找一个子集sum为偶数;

分析:xjb写

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
const int mod=1e9+7;
const ll INF=1e18;
int a[maxn];
void rua()
{
    int n,num=-1;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        if(a[i]%2==0) 
        {
            printf("1\n%d\n",i);
            return;
        }
        else 
        {
            if(num!=-1)
            {
                printf("2\n%d %d\n",num,i);
                return;
            }
            else num=i;
        }
    }
    printf("-1\n");
}
int main()
{
    int t;scanf("%d",&t);
    while(t--) rua();
    return 0;
}

B. Count Subrectangles

题意:矩阵cij=ai*bj,求有多少个面积为k的全1子矩阵;

分析:通过前缀和分别对abO(n)处理出连续为1的不同长度的个数,答案就是k的所有因子对对应个数的乘积;

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
const int mod=1e9+7;
const ll INF=1e18;
int a[maxn],b[maxn],sa[maxn],sb[maxn];
void rua()
{
    int n,m,k;scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    for(int i=0;i<m;i++) scanf("%d",&b[i]);
    int num=0;a[n]=b[m]=0;
    for(int i=0;i<=n;i++)
    {
        if(a[i]==0) num=0;
        else num++,sa[1]++,sa[num+1]--;
    }
    for(int i=0;i<=m;i++)
    {
        if(b[i]==0) num=0;
        else num++,sb[1]++,sb[num+1]--;
    }
    for(int i=1;i<=n;i++) sa[i]+=sa[i-1];
    for(int i=1;i<=m;i++) sb[i]+=sb[i-1];
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        if(k%i!=0) continue;
        int j=k/i;
        if(j>m) continue;
        ans+=1ll*sa[i]*sb[j];
    }
    printf("%lld\n",ans);
}
int main()
{
    rua();
    return 0;
}

C. Unusual Competitions

题意:给一段左右括号的字符串,任意长度子区间可以任意排序,代价为区间长度,求使得括号正确匹配的最小代价;

分析:贪心选择尽可能短的区间处理;s1 s2 分别记下未正确匹配的个数,s1==s2且不为0时,这一段就可以处理;

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
const int mod=1e9+7;
const ll INF=1e18;
char s[maxn];
void rua()
{
    int n;scanf("%d",&n);
    scanf("%s",s+1);
    int s1=0,s2=0,ans=0,l;
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='(') s1++;
        else s2++;
    }
    if(s1!=s2) {printf("-1\n");return;}
    s1=s2=0;l=1;
    if(s[1]=='(') s1++;
    else s2++;
    for(int r=2;r<=n;r++)
    {
        if(s[r]=='(') s1++;
        else
        {
            if(s1) s1--;
            else s2++;
        }
        if(s1==s2) 
        {
            if(s1!=0) ans+=(r-l+1);
            l=r+1;
            s1=s2=0;
        }
    }
    printf("%d\n",ans);
}
int main()
{
    rua();
    return 0;
}

D.Present

题意:给你n个数,求任意两个数和的异或值;

分析:显然能想到二进制运算,由于加法存在进位,所以低位对高位会产生影响,高位对低位则不会,因此考虑从低位向高位处理;每个数都对(1<<(i+1))取模,那么所有数的范围都在[0,1<<(i+1)-1],不考虑比i高的位置;那么最终答案二进制下第i位是否为1就取决于是否有奇数对和在这个位置为1,两两求和可以枚举其中一个,二分找到对第i位产生影响的数的个数;

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=4e5+7;
const int mod=1e9+7;
const ll INF=1e18;
int a[maxn],b[maxn];
void rua()
{
    int n,ans=0;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=0;i<28;i++)
    {
        for(int j=1;j<=n;j++) b[j]=a[j]%(1<<(1+i));
        sort(b+1,b+1+n);
        ll num=0;
        for(int j=1;j<=n;j++)
        {
            //b[j]+b[k]>=(1<<(i+1))+(1<<i)   11xxxx此时都满足
            ll res=lower_bound(b+1,b+1+n,(1<<(i+1))+(1<<i)-b[j])-b-1;
            num+=n-res;
            if(2*b[j]>=(1<<(i+1))+(1<<i)) num--;//排除自己加自己
            //b[j]+b[k]>=(1<<i)   01xxxx/10xxxx/11xxxx 此时只要01xxxx 因为11xxxx已经算过了
            ll s1=lower_bound(b+1,b+1+n,(1<<i)-b[j])-b-1;
            //b[j]+b[k]>=(1<<(i+1))    10xxxx/11xxxx 这些都不要
            ll s2=lower_bound(b+1,b+1+n,(1<<(i+1))-b[j])-b-1;
            num+=(s2-s1);
            if(2*b[j]>=(1<<i) && 2*b[j]<(1<<(i+1))) num--;//排除自己加自己
        }
        num>>=1;//每一对都算了两遍
        if(num&1) ans+=(1<<i);
    }
    printf("%d\n",ans);
}
int main()
{
    rua();
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值