Codeforces Round #626 (Div. 2)

Posted on 2022-03-03 19:08  Capterlliar  阅读(17)  评论(0编辑  收藏  举报

A. Even Subset Sum Problem

题意:给出一组数,求其一个和为偶数的子集。

解:略

代码:

#include <stdio.h>
#include <algorithm>
using namespace std;
#define ll long long
#define maxx 200005
#define maxn 1005
#define maxm 200005
#define eps 0.00000001
#define inf 0x7fffffff
#define mod 998244353
//#define int long long
int n;
int a[maxx];
signed main() {
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        if(n==1&&a[1]%2==1)
            printf("-1\n");
        else {
            for(int i=1;i<=n;i++){
                if(a[i]%2==0){
                    printf("1\n");
                    printf("%d\n",i);
                    goto nxt;
                }
            }
            printf("2\n");
            int cnt=0;
            for(int i=1;i<=n;i++){
                if(a[i]%2){
                    printf("%d ",i);
                    cnt++;
                    if(cnt==2){
                        printf("\n");
                        goto nxt;
                    }
                }
            }
        }
        nxt:;
    }
    return 0;
}
View Code

B. Count Subrectangles

题意:给出两个长度为n的01序列,按矩阵相乘得出一个n*n的矩阵,求矩阵中大小为k的由1组成的长方形有多少。

解:显然只有行和列都为1时对应格为1,也就是说,设a*b=k,则两个数组中须有连续长为a的1和连续长为b的1,将连续段相乘即为答案。(一开始先处理出每个数组各有多少个连续的1,结果一直RE,后面换成要用的时候再找就不RE了,或许还快一点,毕竟k的因子也没几个。。。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxx 1000005
#define maxn 1005
#define maxm 200005
#define eps 0.00000001
#define inf 0x7fffffff
#define mod 998244353
//#define int long long
int n,m,k;
char a[maxx];
int b[maxx]={0};
signed main() {
    scanf("%d",&n);
    scanf("%s",a+1);
    int l=0,r=0;
    for(int i=1;i<=n;i++){
        if(a[i]=='(')
            l++;
        else r++;
        b[i]=l-r;
    }
    if(l!=r){
        printf("-1\n");
        return 0;
    }
    int pos=0,ans=0;
    for(int i=1;i<=n;i++){
        if(b[i]==0){
            if(b[i-1]==1) {
                pos=i;
                continue;
            }
            ans+=i-pos;
            pos=i;
        }
    }
    printf("%d\n",ans);
    return 0;
}
View Code

C. Unusual Competitions

题意:给定一个括号序列,每次可选一个子区间随意排列,问至少重排多长的序列后可以使整串括号合法。

解:既然不是问次数,而是问总长度,那看到不合法的序列,找到能让它合法的位置,就重排。合法的位置很好找,当区间内左右括号数量相等时即可。考虑判断原来就合法的序列有什么特点,当然可以用栈,但其实观察左右括号个数,如果截止到前一个括号,左比右多一个,那一定合法。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxx 1000005
#define maxn 1005
#define maxm 200005
#define eps 0.00000001
#define inf 0x7fffffff
#define mod 998244353
//#define int long long
int n,m,k;
char a[maxx];
int b[maxx]={0};
signed main() {
    scanf("%d",&n);
    scanf("%s",a+1);
    int l=0,r=0;
    for(int i=1;i<=n;i++){
        if(a[i]=='(')
            l++;
        else r++;
        b[i]=l-r;
    }
    if(l!=r){
        printf("-1\n");
        return 0;
    }
    int pos=0,ans=0;
    for(int i=1;i<=n;i++){
        if(b[i]==0){
            if(b[i-1]==1) {
                pos=i;
                continue;
            }
            ans+=i-pos;
            pos=i;
        }
    }
    printf("%d\n",ans);
    return 0;
}
View Code

D. Present

题意:很粗暴的题意,计算:

,0<=n<=4e5

 

 

解:昨天做的时候一直试图给它们配对,看看有什么能抵消的。。。以后看见异或还是按位拆吧。

这串东西的意思是每两个数都结合一遍。也就是说把它们排成一列,每两个都能拎出来加一加。考虑第k位是不是1。枚举每个数,看有多少数和其相加后第k位为1。第k位为1,第k+1位,k+2位也有可能为1,也就是两者和有两种可能,[2k,2k+1-1]和[2k+2k+1,2k+2-2]。现在要算这两个区间内有多少数,应当用二分查找。二分查找要排序,由于现在只看末k+1位,还要先模一下。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxx 400005
#define maxn 1005
#define maxm 200005
#define eps 0.00000001
#define inf 0x7fffffff
#define mod 998244353
//#define int long long
int n;
int a[maxx]={0};
int b[maxx];
int p[30];
int cal(int k){
    for(int i=1;i<=n;i++)
        b[i]=a[i]%(1<<(k+1));
    sort(b+1,b+n+1);
    int res=0;
    for(int i=1;i<=n;i++){
        res+=upper_bound(b+i+1,b+n+1,p[k+1]-1-b[i])-
             lower_bound(b+i+1,b+n+1,p[k]-b[i]);
        res+=upper_bound(b+i+1,b+n+1,p[k+2]-2-b[i])-
             lower_bound(b+i+1,b+n+1,p[k]+p[k+1]-b[i]);
    }
    return res%2;
}
// k k+1 k+2
// [2^k,2^(k+1)-1]
// [2^k+2^(k+1),2^(k+2)-2]
signed main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=0;i<=26;i++)
        p[i]=1<<i;
    int ans=0;
    for(int i=0;i<=24;i++){
        if(cal(i))
            ans+=1<<i;
    }
    printf("%d\n",ans);
    return 0;
}
View Code

E. Instant Noodles

建议改名随意起名谜语人场(

题意:有一个二分图,左边n个结点,右边n个结点。右边每个结点都有一个权值。取左边结点的一个子集,令N(S)为所有与子集中点相连的右部结点,f(S)为N(S)的权值和。求所有f(S)的gcd。

解:看起来有很多子集,很麻烦,好在求的是gcd。已知gcd(a,b)=gcd(a+b,b)=gcd(a+b,a)。也就是说,有了a和b,就不用求a+b了,只要找出能合并出全集的几个集合就可以了。有一个朴素的想法,将每一个左部顶点对应的右部顶点之和求出来,一起gcd。但是有一个问题,当两个结点共用一个右部结点时它们的和就不是a+b了,所以不行。详见样例2。因此考虑合并右部结点。条件挺苛刻的,两个右部结点,如果对应左部结点完全相同才能合并,这样一来就没有会被共用的点了,可以直接求gcd。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxx 500005
#define maxn 1005
#define maxm 200005
#define eps 0.00000001
#define inf 0x7fffffff
#define mod 998244353
//#define int long long
int n,m;
ll a[maxx];
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
signed main() {
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        int x,y;
        map<int,set<int> > m1;
        for(int i=1;i<=m;i++) {
            scanf("%d%d",&x,&y);
            m1[y].insert(x);
        }
        map<set<int>,ll> m2;
        for(auto [x,s]:m1)
            m2[s]+=a[x];
        ll ans=0;
        for(auto [s,x]:m2)
            ans=gcd(ans,x);
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

(用vector没过,用set过了,怪耶