题目链接: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,请你选择一个颜***使得在任一时刻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,没读