Codeforces 1323 div2 题解 (ABC)

比赛链接:https://codeforces.com/contest/1323/

日常记录自己的菜鸡行径

虽然我打的是div1,但是我深刻认识到我水平其实只有div2的(确信),不然我也不会狂掉101分

A. Even Subset Sum Problem

签到题,略

#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
typedef long long ll; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;}
const int N=1000010;
vector<int> a[2];
signed main(){
    ios_base::sync_with_stdio(0); cin.tie(0);
    int t=read();
    while(t--){
        a[0].clear();
        a[1].clear();
        int n=read();
        repeat(i,1,n+1){
            int x=read()%2;
            a[x].push_back(i);
        }
        if(!a[0].empty())cout<<1<<endl<<a[0][0]<<endl;
        else if(a[1].size()>1)cout<<2<<endl<<a[1][0]<<' '<<a[1][1]<<endl;
        else cout<<-1<<endl;
    }
    return 0;
}

B. Count Subrectangles

这题居然在B题位置

首先要统计一下序列 \(a\)\(b\) 的所有连续 \(1\) 区间长度,记 \(A_i\)\(a\) 序列第 \(i\) 个连续 \(1\) 的区间长度,\(B_i\) 类似

考虑 \(k\) 的一个约数 \(d\),那么 \(d\times\dfrac k d\) 矩形的个数就是大于 \(0\)\((A_i-d+1)\) 之和乘以大于 \(0\)\((B_i-\dfrac k d+1)\) 之和,记这个数为 \(cnt(d)\)

最终答案就是所有的 \(cnt(d)\) 加起来,即 \(\sum_{d|k}cnt(d)\)

加速的话可以将 \(A_i\)\(B_i\) 排个序后求前缀和,用上 lower_bound

总时间复杂度大概 \(O(\sqrt{nm}\log(nm))\)

#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
typedef long long ll; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;}
const int N=40010;
vector<int> divisor;
int a[N],b[N],a_sum[N],b_sum[N]; //这个a,b实际上是题解中的A,B
void get_divisor(int n){ //求出所有约数,这是我研究出来(大雾)的数论分块求法
    for(int i=1;i<n;i=n/(n/(i+1)))
    if(n%i==0)
        divisor.push_back(i);
    divisor.push_back(n);
}
int input(int a[],int a_sum[],int n){ //读入、排序、计算前缀和
    int len=0,cnt=0;
    a[cnt++]=0;
    repeat(i,0,n){
        if(read())len++;
        else if(cnt>0)a[cnt++]=len,len=0;
    }
    if(len>0)a[cnt++]=len;
    sort(a,a+cnt);
    repeat(i,1,cnt)a_sum[i]=a_sum[i-1]+a[i];
    return cnt;
}
ll count(int a[],int a_sum[],int n,int d){ //计算所有大于0的(ai-d+1)之和
    int p=lower_bound(a,a+n,d)-a;
    ll ans=a_sum[n-1]-a_sum[p-1]-(n-p)*(d-1);
    return ans;
}
signed main(){
    ios_base::sync_with_stdio(0); cin.tie(0);
    int n=read(),m=read();
    ll k=read(); get_divisor(k);
    n=input(a,a_sum,n); m=input(b,b_sum,m);
    ll ans=0;
    for(auto d:divisor){
        ans+=count(a,a_sum,n,d)*count(b,b_sum,m,k/d);
    }
    cout<<ans<<endl;
    return 0;
}

C. Unusual Competitions

div1签到题

括号序列的一种常见处理方式是把 '(' 看作 \(1\) ,把 ')' 看作 \(-1\),然后求前缀和

这时候前缀和小于 \(0\) 的区间(再加上区间之后的那个位置)就是需要重排的区间,把区间长度 \(+1\) 计入答案中

比如样例中的 ))((())(​ 有:

) ) ( ( ( ) ) (
括号对应的值 -1 -1 1 1 1 -1 -1 1
前缀和 -1 -2 -1 0 1 0 -1 0
是否要重排 × ×

(如果前缀和最后一个位置不是0直接输出-1就行了)

#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
const int N=1000010;
string s;
int H[N],*h=H+1,n,ans=0;
signed main(){
    ios_base::sync_with_stdio(0); cin.tie(0);
    cin>>n>>s;
    repeat(i,0,n){
        h[i]=h[i-1]+(s[i]=='(')*2-1;
    }
    if(h[n-1]!=0)cout<<-1<<endl,exit(0);
    repeat(i,0,n)ans+=(h[i]<0)+(h[i]<0 && h[i-1]>=0);
    cout<<ans<<endl;
    return 0;
}

D. Present

div1的第二题,我成功被难住

至今思路还不是很健全,明天再更qwq

猜你喜欢

转载自www.cnblogs.com/axiomofchoice/p/12439352.html
今日推荐