Educational Codeforces Round 24 题解

来源:互联网 发布:php打印九九乘法表 编辑:程序博客网 时间:2024/06/01 09:03

题目链接:http://codeforces.com/contest/818/problem/A

A:数学,水题。

#include <bits/stdc++.h>using namespace std;typedef long long LL;int main(){    LL n, k;    scanf("%lld%lld",&n,&k);    LL x = n/(2*(k+1));    if(x == 0){        printf("0 0 %lld\n", n);    }    else{        printf("%lld %lld %lld\n", x, k*x, n-(k+1)*x);    }}

B,水题,模拟。题意:给出一个排列a1…an,进行m轮游戏,首先选定一个leader作为l[1],l[i+1]=a[l[i]]+l[i] a[l[i]]+l[i]=l[i+1] 所以 a[l[i]]=l[i+1]-l[i] 如果遇到有冲突的情况特判一下输出-1(一个以上的位置是同一个数或同一个位置几次得到的结果不一样)

#include <bits/stdc++.h>using namespace std;int n, m, l[110], a[110], ans[110], temp[110];int vis[110];int main(){    scanf("%d%d",&n,&m);    for(int i=1; i<=m; i++) scanf("%d",&l[i]);    bool flag = 0;    for(int i=1; i<=n; i++){        int st = l[1];        memset(vis, 0, sizeof(vis));        memset(temp, 0, sizeof(temp));        int pos = 1;        temp[1] = i;        vis[i] = 1;        bool ok = 1;        for(int j=2; j<=m; j++){            if(temp[st]==0){                int x = l[j]-l[j-1];                if(x<=0) x+=n;                if(x>n) x-=n;                temp[st]=x;                ++vis[x];                st = l[j];            }            else{                int x = temp[st]+st;                if(x>n) x-=n;                if(x==l[j]){                    st = l[j];                }                else{                    ok = 0;                }            }        }        for(int j=1; j<=n; j++){            if(vis[j]>1){                ok = 0;                break;            }        }        if(ok){            for(int j=1; j<=n; j++){                if(temp[j]) ans[j] = temp[j];                else{                    for(int k=1; k<=n; k++){                        if(!vis[k]){                            ans[j] = k;                            vis[k] = 1;                            break;                        }                    }                }            }            flag = 1;            break;        }    }    if(flag==0){        puts("-1");    }    else{        for(int i=1; i<=n; i++) printf("%d ", ans[i]);        printf("\n");    }    return 0;}

C:有d个沙发,每个沙发占据相邻两个格子,定义一个沙发A在另一个沙发B左边为:存在沙发A的格子a和沙发B的格子b使得xa小于xb,求出一个沙发满足给出的cntl,cntr,cntt,cntb m和n都不超过1e5,直接处理出前缀和,O(n)可求

#include <bits/stdc++.h>using namespace std;const int maxn = 100010;struct node{    int x1,y1,x2,y2;}sofa[maxn];int d, n, m, cntl, cntr, cntt, cntb, ans, sum1[maxn], sum2[maxn], sum3[maxn], sum4[maxn];int main(){    scanf("%d%d%d", &d,&n,&m);    for(int i=1; i<=d; i++){        scanf("%d%d%d%d", &sofa[i].x1,&sofa[i].y1,&sofa[i].x2,&sofa[i].y2);        sum1[min(sofa[i].x1,sofa[i].x2)]++;        sum2[max(sofa[i].x1,sofa[i].x2)]++;        sum3[min(sofa[i].y1,sofa[i].y2)]++;        sum4[max(sofa[i].y1,sofa[i].y2)]++;    }    scanf("%d%d%d%d", &cntl,&cntr,&cntt,&cntb);    for(int i=1; i<=n; i++) sum1[i]+=sum1[i-1];    for(int i=n; i>=1; i--) sum2[i]+=sum2[i+1];    for(int i=1; i<=m; i++) sum3[i]+=sum3[i-1];    for(int i=m; i>=1; i--) sum4[i]+=sum4[i+1];    ans = -1;    for(int i=1; i<=d; i++){        int l = sum1[max(sofa[i].x1, sofa[i].x2)-1];        int r = sum2[min(sofa[i].x1, sofa[i].x2)+1];        if(sofa[i].x1 != sofa[i].x2) l--, r--;        int t = sum3[max(sofa[i].y1, sofa[i].y2)-1];        int b = sum4[min(sofa[i].y1, sofa[i].y2)+1];        if(sofa[i].y1 != sofa[i].y2) t--,b--;        if(l == cntl && r == cntr && t == cntt && b == cntb){            ans = i;            break;        }    }    printf("%d\n", ans);    return 0;}

D,有n辆车依次驶过,给定对方选择的一个颜色a,请你选择一个颜色b使得在任一时刻cntb>=cnta。

解法:这道题本来O(n)扫一遍就好了,傻逼强行怼了线段树。我的做法就是对所有的颜色开一个线段树,当颜色等于A的时候所有颜色的值-1,不等于A的时候,如果当前颜色所在位置的值>=0的话,给当前值+1,最后查找这个线段树有没有>=0的节点,有的话输出这个值就好了。

#include <bits/stdc++.h>using namespace std;const int maxn = 1e6+7;int n, A, c[maxn];struct node{    int l,r,len,sum;    int lazy;}tree[maxn*4];void pushup(int rt){    tree[rt].sum = tree[rt*2].sum+tree[rt*2+1].sum;}void pushdown(int rt){    if(tree[rt].lazy){        tree[rt*2].lazy+=tree[rt].lazy;        tree[rt*2+1].lazy+=tree[rt].lazy;        tree[rt*2].sum += tree[rt*2].len*tree[rt].lazy;        tree[rt*2+1].sum += tree[rt*2+1].len*tree[rt].lazy;        tree[rt].lazy = 0;    }}void Build(int l, int r, int rt){    tree[rt].l = l, tree[rt].r = r, tree[rt].sum = 0, tree[rt].lazy = 0, tree[rt].len = r-l+1;    if(l == r){        return;    }    int mid = (l+r)/2;    Build(l,mid,rt*2);    Build(mid+1,r,rt*2+1);    pushup(rt);}void update(int L, int R, int c, int rt){    if(L<=tree[rt].l&&tree[rt].r<=R){        tree[rt].sum += c*(tree[rt].len);        tree[rt].lazy += c;        return;    }    pushdown(rt);    int mid = (tree[rt].l+tree[rt].r)/2;    if(R<=mid) update(L,R,c,rt*2);    else if(L>mid) update(L,R,c,rt*2+1);    else{        update(L,mid,c,rt*2);        update(mid+1,R,c,rt*2+1);    }    pushup(rt);}int query(int L, int R, int rt){    if(L<=tree[rt].l&&tree[rt].r<=R){        return tree[rt].sum;    }    pushdown(rt);    int mid = (tree[rt].l+tree[rt].r)/2;    if(R<=mid) return query(L,R,rt*2);    else if(L>mid) return query(L,R,rt*2+1);    else return query(L,mid,rt*2)+query(mid+1,R,rt*2+1);}int main(){    scanf("%d%d",&n,&A);    for(int i=1; i<=n; i++) scanf("%d", &c[i]);    Build(1,1000000,1);    int ans = -1;    for(int i=1; i<=n; i++){        if(c[i]==A){            update(1,1000000,-1,1);        }        else{            if(query(c[i],c[i],1)>=0){                update(c[i],c[i],1,1);            }        }    }    for(int i=1; i<=1000000; i++){        if(i==A) continue;        if(query(i,i,1)>=0){            ans = i;            break;        }    }    printf("%d\n", ans);    return 0;}

E,有多少连续的ai…aj的乘积是k的倍数
解法:将k分解质因数,处理出序列每个质因数个数的前缀和,对于每个左端点通过二分来得到最小的能使乘积为k的倍数的右端点

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 400010;LL n, k;LL a[maxn];LL num1[20], num2[20], cnt=0;LL sum[maxn][20];bool check(LL x, LL y){    for(int i=0; i<cnt; i++){        LL temp = sum[y][i]-sum[x-1][i];        if(temp<num2[i]) return false;    }    return true;}int main(){    scanf("%lld%lld", &n,&k);    LL x = k;    for(LL i=2; i*i<=x; i++){        if(x%i==0){            int p = 0;            num1[cnt++] = i;            while(x%i==0){                x/=i;                p++;            }            num2[cnt-1] = p;        }    }    if(x > 1){        num1[cnt++] = x;        num2[cnt-1] = 1;    }    memset(sum, 0, sizeof(sum));    for(LL i=1; i<=n; i++){        scanf("%lld", &a[i]);        for(int k = 0; k < cnt; k++){            sum[i][k] = sum[i-1][k];            while(a[i]%num1[k]==0){                sum[i][k]++;                a[i]/=num1[k];            }        }    }    LL anss = 0;    for(LL i=1; i<=n; i++){        LL l = i, r = n, ans = n;        while(l<=r){            LL mid = (l+r)/2;            if(check(i, mid)) ans = mid, r=mid-1;            else l = mid+1;        }        if(check(i, ans)) anss = anss+n-ans+1;    }    printf("%lld\n", anss);    return 0;}

F,n个点的图,桥的个数要求不小于边数的一半,问边数的最大值
解法:n个点中k个点两两相连有k*(k-1)/2条边,其余的点顺次相连为桥n-k条边,当k*(k-1)/2<=n-k时才符合要求 可以发现f(k)=n-k+min(n-k,k*(k-1)/2)是一个单峰函数,于是三分得到答案

#include <bits/stdc++.h>using namespace std;typedef long long LL;int q;LL n, ans;LL check(LL mid){    return n-mid+min(n-mid,(mid-1)*mid/2);}int main(){    scanf("%d", &q);    while(q--){        scanf("%lld", &n);        LL l = 1, r = n;        while(l + 1 < r){            LL mid = (l+r)/2;            LL midd = (mid+r)/2;            if(check(mid)<check(midd)) l = mid;            else r = midd;        }        printf("%lld\n", max(check(l),check(l+1)));    }}

G,没读

原创粉丝点击