Codeforces Round #737 (Div. 2)

Codeforces Round #737 (Div. 2)

A. Ezzat and Two Subsequences

在这里插入图片描述

题意:把两个序列拆分成两个部分,对两个部分做平均值,让两个平均值相加尽可能最大。

题解:可以观察到,将最大的数单独分一个出来,剩下的数一个部分求平均值,然后相加就会最大。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+50;
const int INF = 0x3f3f3f3f;

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        ll maxs = -INF, sums = 0, x;
        scanf("%d",&n);
        for(int i = 0;i < n;i++){
            scanf("%lld",&x);
            sums += x;
            maxs = max(maxs,x);
        }
        // printf("%lld %lld\n",maxs,sums);
        printf("%.9lf\n",maxs+(sums-maxs)*1.0/(n-1));
    }
    return 0;
}

B. Moamen and k-subarrays

在这里插入图片描述
题意:将一个序列分成 k k k个连续的互不相交的子序列。然后问可不可以通过改变这 k k k序列的相对顺序,使得拼完之后的整个序列可以变为从小到大的有序序列。

题解:这个题目只要找先离散化一下,然后在序列里找一下,从小到大,并在离散化后的表中连续的部分有多少个,如果小于等于 k k k则有解,否则无解。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+50;
const int INF = 0x3f3f3f3f;

vector<int> has;
int a[N];

int find(int x){
    return lower_bound(has.begin(),has.end(),x)-has.begin();
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        has.clear();
        int n, k, x = INF;
        scanf("%d %d",&n,&k);
        for(int i = 0;i < n;i++){
            scanf("%d",&a[i]);
            has.push_back(a[i]);    
        }
        sort(has.begin(),has.end());
        for(int i = 0;i < n;i++){
            if(x > a[i] || find(a[i]) - find(x) > 1) k--;
            // printf("%d %d %d\n",x,a[i],k);
            x = a[i];
        }
        if(k >= 0) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

C. Moamen and XOR

在这里插入图片描述
题意:给你一个长度为 n n n的序列,序列中每个数的大小范围为 [ 0 , 2 k ) [0,2^k) [0,2k),然后问你将这个序列所有的数按位与的值会大于按位异或的值的序列有多少个。

题解:观察一下可以发现,这题可以用数位dp来处理,首先无论是 & \& &还是 ⨁ \bigoplus 都不会对其他位造成影响,所以可以按位处理。

然后你根据数位dp的处理方式,从高位往低位枚举,如果 n n n为偶数,那么按位处理到第 i i i位时,所有数改位全部取 1 1 1,则 ⨁ \bigoplus 的该位值位0, & \& &的该位值为1,所有后面低位的所有的位无论取啥 & \& &的值都会更大,所以算一下 2 k − i 2^{k-i} 2ki乘一下前面的情况数加上就好了,然后如果 n n n为奇数,则两个都会为 1 1 1,只能枚举到最后一位才能取相等的值最后加一下,然后在注意一下这一位两个都为0的情况,用组合数计算一下即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+50;
const int INF = 0x3f3f3f3f, mod = 1e9 + 7;

vector<int> has;
int a[N];
ll f[N], fact[N], infact[N], sums;

ll qmi(ll x,int k){
    ll res = 1;
    while(k){
        if(k&1) res = (res * x) % mod;
        x = (x * x) % mod;
        k >>= 1;
    }
    return res;
}

void getfact(){
    fact[0] = infact[0] = 1;
    for(int i = 1;i < N;i++){
        fact[i] = fact[i-1] * i % mod;
        infact[i] = infact[i-1] * qmi(i,mod-2) % mod;
    }
}
ll C(int x, int y){
    if(x < y) return 0;
    else return (fact[x] * infact[y] % mod) * infact[x-y] % mod;
}

void Init(int n, int k){
    f[0] = 1;
    f[1] = qmi(2,n);
    for(int i = 2;i < k;i++) f[i] = (f[i-1]*f[1])%mod;
    for(int i = 0;i < n;i+=2){
        sums = (C(n,i) + sums) % mod;
        // printf("%lld %lld %d %d\n",sums,C(n,i),n,i);
    }
}

int main(){
    int t;
    getfact();
    // for(int i = 0;i <= 10;i++) printf("f%lld %lld\n",fact[i],infact[i]); printf("\n");
    scanf("%d",&t);
    while(t--){
        sums = 0;
        ll ans = 0, res = 1;
        int n, k;
        scanf("%d %d",&n,&k);
        if(k == 0){
            printf("1\n");
            continue;
        }
        Init(n,k);
        // printf("sums = %lld\n",sums);
        for(int i = k;i > 1;i--){
            if((n&1) == 0) ans = (ans + (res*f[i-1]) % mod) % mod;
            if(n&1) res = res * (sums + 1) % mod;
            else res = res * sums % mod;
            // printf("%lld %lld %lld\n",ans,res,f[i-1]);
        }
        ans = (ans + ((sums+1)*res)%mod) % mod;
        printf("%lld\n",ans);
    }
    return 0;
}

D. Ezzat and Grid

在这里插入图片描述
题意:就是给你 n n n个从上到下排列的0/1串,初始时全为 0 0 0,然后给你 m m m个区间告诉你把第 i i i行第 l l l列到第 r r r列全部附为 1 1 1,最后他要你删掉最少的几行,使得删完之后的行相邻行之间至少某一相同位都有一个 1 1 1

题解:可以发现这个题可以通过和枚举到某一行,并和他上面的行比较是否有 1 1 1的交集,如果有的话可以连一条有向边,最后只要走一个最长路径就可以了。

但是这个图可能是个完全图,所以直接建边是不可能的,所以我们考虑只建当前和这一行相交的路径最长的那一行的边就可以了,但是逐个比较还是 O ( n m ) O(nm) O(nm)复杂度。然后发现找区间相交的最大值,就是找区间最大值,可以考虑用线段树优化。于是就可以将复杂度优化到 O ( m l o g ( n ) ) O(mlog(n)) O(mlog(n)),然后注意数据范围需要离散化。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int N = 3e5+50;
const int INF = 0x3f3f3f3f, mod = 1e9 + 7;

bool flag[N];
int pre[N];
vector<int> has;
struct Node{
    int i, l, r;
    friend bool operator <(Node a,Node b){
        if(a.i == b.i) return a.l < b.l;
        return a.i < b.i;
    }
}seg[N];

struct QAQ{
    int l, r, maxs, lazy, maxid;
}tree[N*8];

int find(int x){
    return lower_bound(has.begin(),has.end(),x) - has.begin();
}

void build(int rt, int l, int r){
    tree[rt].l = l; tree[rt].r = r;
    tree[rt].lazy = tree[rt].maxs = tree[rt].maxid = 0;
    if(l == r) return;
    int mid = l+r>>1;
    build(rt<<1, l, mid); build(rt<<1|1, mid+1, r);
}

void pushdown(int rt){
    if(tree[rt].lazy){
        tree[rt<<1].maxs = tree[rt<<1|1].maxs = tree[rt].lazy;
        tree[rt<<1].lazy = tree[rt<<1|1].lazy = tree[rt].lazy;
        tree[rt<<1].maxid = tree[rt<<1|1].maxid = tree[rt].maxid;
        tree[rt].lazy = 0;
    }
}

void update(int rt,int l,int r,int x,int id){
    if(l <= tree[rt].l && r >= tree[rt].r){
        tree[rt].maxs = tree[rt].lazy = x;
        tree[rt].maxid = id;
        return;
    }
    pushdown(rt);
    int mid = tree[rt].l + tree[rt].r >> 1;
    if(mid >= l) update(rt<<1,l,r,x,id);
    if(r > mid) update(rt<<1|1,l,r,x,id);
    if(tree[rt<<1].maxs > tree[rt<<1|1].maxs){
        tree[rt].maxs = tree[rt<<1].maxs;
        tree[rt].maxid = tree[rt<<1].maxid;
    }
    else{
        tree[rt].maxs = tree[rt<<1|1].maxs;
        tree[rt].maxid = tree[rt<<1|1].maxid;
    }
}

P query(int rt,int l,int r){
    if(l <= tree[rt].l && r >= tree[rt].r) return P(tree[rt].maxs,tree[rt].maxid);
    pushdown(rt);
    int mid = tree[rt].l + tree[rt].r >> 1;
    P mx = P(0,0);
    if(mid >= l) mx = query(rt<<1,l,r);
    if(r > mid) mx = max(mx,query(rt<<1|1,l,r));
    return mx;
}

int main(){
    int n, m;
    scanf("%d %d",&n,&m);
    has.push_back(0);
    for(int i = 0;i < m;i++){
        scanf("%d %d %d",&seg[i].i,&seg[i].l,&seg[i].r);
        has.push_back(seg[i].l);
        has.push_back(seg[i].r);
    }
    sort(has.begin(), has.end());
    has.erase(unique(has.begin(), has.end()), has.end());
    int sz = has.size();
    sort(seg, seg+m);
    build(1,1,sz);
    int lz = 0, row = seg[0].i, mx = 0, maxrow = 0;
    P temp;
    for(int i = 0;i < m;i++){
        if(seg[i].i != row){
            for(int j = lz;j < i;j++){
                update(1,find(seg[j].l),find(seg[j].r),mx+1,row);
            }
            pre[row] = maxrow;
            maxrow = 0;
            row = seg[i].i;
            lz = i;
            mx = 0;
        }
        temp = query(1,find(seg[i].l),find(seg[i].r));
        if(mx < temp.first){
            mx = temp.first; maxrow = temp.second;
        }
    }
    for(int i = lz;i < m;i++) update(1,find(seg[i].l),find(seg[i].r),mx+1,row);
    pre[row] = maxrow;
    temp = query(1,1,sz);
    mx = temp.first; maxrow = temp.second;
    while(maxrow){
        flag[maxrow] = true;
        maxrow = pre[maxrow];
    }
    printf("%d\n",n-mx);
    for(int i = 1;i <= n;i++){
        if(!flag[i]) printf("%d ",i);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值