• 暑期训练 CF套题


    CodeForces 327A

    题意:有n个数,都是0或1,然后必须执行一次操作,翻转一个区间,里面的数0变1,1变0,求最多1的数量

    思路:最开始我写的最大字段和,后面好像写搓了,然后我又改成暴力,因为这个范围只有100,写n^3都没事,所以我们第一层枚举左区间,第二层枚举右区间,然后我们第三层记录左边1的数量,中间的0的数量,右边的1的数量即可

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 100005
    #define mod 1000000007
    using namespace std;
    typedef long long ll;
    ll a[1005],num,n;
    int main(){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        ll mx=0;
        for(int i=1;i<=n;i++){
            for(int j=i;j<=n;j++){
                ll shu=0;
                for(int k=1;k<i;k++){
                    if(a[k]) shu++;
                 }
                 for(int k=i;k<=j;k++){
                    if(a[k]==0) shu++;
                 }
                 for(int k=j+1;k<=n;k++){
                    if(a[k]) shu++;
                 }
                 mx=max(shu,mx);
            }
        }
        printf("%lld",mx);
    } 
    View Code

    CodeForces 854C

    题意:有n架飞机,现在因为一些原因飞机都只能拖延到k+1时刻后出发,一个时刻只能出发一架,每架飞机有个晚飞一分钟损失多少,最开始飞机是按顺序安排的时刻表,现在问你怎么排表损失最小

    思路:首先我们知道最开始是 1 2 3 4 5,如果k为2,那么我们只能是 3 4 5 6 7 ,但是我们要怎么安排呢,我们能清晰的知道,每个人都是一分钟损耗多少,所以我们肯定是让最大的尽量少损失,因为差值都是固定的  3 4 5 6 7 - 1 2 3 4 5  差值和为10  ,只是我们现在要排列把这个差值给哪些数,我们肯定把差值尽量给损失小的,所以我们排个序,我们找最接近当前数的值,但是我们找又是个问题,一个只能用一次,用完就要删除,而且我们不能遍历找,符合这些要求的有个很好的东西就是 set,我们可以利用log级别的删除,set.lower_bound log级别的查找就能很好解决这个事

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #define maxn 300005
    #define mod 1000000007
    using namespace std;
    typedef long long ll;
    struct sss
    {
        ll id;
        ll x;
    }a[maxn];
    ll n,m;
    ll vis[maxn];
    set<ll> q;
    int cmp(struct sss x,struct sss y){
        return x.x>y.x;
    }
    int main(){
        scanf("%lld%lld",&n,&m);
        for(int i=1;i<=n;i++){
            q.insert(i+m);
        }
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i].x);
            a[i].id=i;
        }
        sort(a+1,a+n+1,cmp);
        ll sum=0;
        for(int i=1;i<=n;i++){
            ll x=*q.lower_bound(a[i].id);
            //set<ll>::iterator t1=q.upper_bound(a[i].x);    
            sum+=(x-a[i].id)*a[i].x;
            vis[a[i].id]=x;
            q.erase(x);
        }
        printf("%lld
    ",sum);
        for(int i=1;i<=n;i++){
            printf("%lld ",vis[i]);
        }
    } 
    View Code

    CodeForces 863C

    题意:有两个人玩石头剪刀布,最开始两个人会选择出什么,然后两个人都会根据对方和自己现在出的东西而改变下次出的,然后会进行k轮游戏,输出每个人赢得次数

    思路:这个k特别大,但是情况最多有9种,也就是说9种过后必定会出现重复的,这个时候说明有循环节,我们把中间周期除掉计算即可

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #define maxn 300005
    #define mod 1000000007
    using namespace std;
    typedef long long ll;
    struct sss
    {
        ll sum1,sum2;
    }c[maxn];//记录当前步的比赛状况
    ll A[4][4];
    ll B[4][4]; 
    ll vis[4][4];
    ll chuan(ll a,ll b){
        if(a==b) return 0;
        if(a==1&&b==2) return -1;
        if(a==1&&b==3) return 1;
        if(a==2&&b==3) return -1;
        if(a==2&&b==1) return 1;
        if(a==3&&b==2) return 1;
        if(a==3&&b==1) return -1;
    }
    int main(){
        ll k,a,b;
        scanf("%lld%lld%lld",&k,&a,&b);
        for(int i=1;i<=3;i++){
            for(int j=1;j<=3;j++){
                scanf("%lld",&A[i][j]);
                vis[i][j]=-1;
            }
        }
        for(int i=1;i<=3;i++){
            for(int j=1;j<=3;j++){
                scanf("%lld",&B[i][j]);
            }
        }
        ll sum1=0,sum2=0;
        ll z=0,flag=0;
        while(z<k){
            ll w=chuan(a,b);
            if(w==1) sum1++;
            else if(w==-1) sum2++;
            if(vis[a][b]!=-1){//找到循环节
                ll len=z-vis[a][b];
                ll sy=k-z-1;
                sum1+=sy/len*(sum1-c[vis[a][b]].sum1);
                sum2+=sy/len*(sum2-c[vis[a][b]].sum2);
                sy%=len;
                k=sy;
                flag=1;
                ll a1=a,b1=b;
                a=A[a1][b1];
                b=B[a1][b1];
                break;
            }
            c[z].sum1=sum1;
            c[z].sum2=sum2;
            vis[a][b]=z;
            ll a1=a,b1=b;
            a=A[a1][b1];
            b=B[a1][b1];
            z++;
        }
        z=0;
        while(flag&&z<k){
            ll w=chuan(a,b);
            if(w==1) sum1++;
            else if(w==-1) sum2++;
            ll a1=a,b1=b;
            a=A[a1][b1];
            b=B[a1][b1];
            z++;
        }
        printf("%lld %lld",sum1,sum2);
    } 
    View Code

    CodeForces 749D

    题意:最开始有n个人,每个人会选择一个团队,自身带一个价值,然后下面有q次查询,说明这次比拼哪些团队不会参加,然后选获胜团队以及获胜团队里面最低能战胜其他所有人的人的价值

    思路:我们首先用vector记录所以人的值,这肯定要做的,然后我们会发现我们要战胜肯定是要把其他所有人的最大值战胜,所以我们用一个set记录所有团队的最大值,我们缺席的话直接暴力

    在set里面删除,然后我们再找出当前最大,那就是获胜者,然后找出次大,然后在获胜者团队二分找出正好比他大的值即可

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<set>
    #include<utility>
    #include<algorithm>
    using namespace std;
    #define mp make_pair
    #define X first
    #define Y second
    const int N=200008;
    int n,m,q,a[N],b[N];
    int ban[N],c[N];
    set<int>d[N];
    pair<int,int>e[N];
    int main(void)
    {
        int i,p1,p2;
        scanf("%d",&n);
        for(i=1;i<=n;i++){scanf("%d%d",&p1,&p2);b[p1]=p2;d[p1].insert(p2);}
        for(i=1;i<=n;i++)e[i]=mp(b[i],i);
        sort(e+1,e+1+n);
        scanf("%d",&q);
        while(q--)
        {
            scanf("%d",&m);
            for(i=1;i<=m;i++){scanf("%d",c+i);ban[c[i]]=1;}
            p1=p2=0;
            for(i=n;i>=1;i--)
            {
                if(0==e[i].X)break;
                if(ban[e[i].Y])continue;
                if(p1){p2=e[i].Y;break;}
                else p1=e[i].Y;
            }
            if(p1==0)printf("0 0
    ");
            else if(p2==0)printf("%d %d
    ",p1,*d[p1].begin());
            else printf("%d %d
    ",p1,*d[p1].lower_bound(*(--d[p2].end())));
            for(i=1;i<=m;i++)ban[c[i]]=0;
        }
        return 0;
    }
    View Code

    CodeForces 926E

    题意:开始有 n个数,然后如果有相同的连续的数x我们要合并,合并之后之前两个数删除然后插入x+1,然后直到不能再合并,下标是左边那个的下标,输出最后结果

    思路:我们会发现合并必须要从低到高执行,因为新插入的是x+1,所以我们必须从低到高,然后我们要考虑的问题是,合并后,但是我的数组其实还是一样的位置,比如 1 1 2,我合并第 1 2个数数组里面就会变成  2 0 2 ,0代表这个位置没数了,但是我们下次合并又要怎么知道1 3个数可以合并呢,暴力肯定不现实,这个时候我们又会发现其实每个数也就和他的下个数有关,我们想用删除一个数,又要查询两个数是否相连,有个很适合这个结构,那就是链表,我们清晰的记录下一个下标是谁,然后上面我们合并后又要插入进去,那肯定是使用优先队列了,然后我们相等的值按下标排序,如果当前两个数不匹配,那么说前面这个数的位置肯定确定下来了。因为要么是下标不符合,中间隔了数,肯定不能再合并。要么是值不相等。从低到高,已经没别的合并的机会了

    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<iostream>
    #include<algorithm> 
    #define maxn 200005
    #define mod 1000000007
    using namespace std;
    typedef long long ll;
    struct sss{
        ll id;
        ll next;
        friend bool operator <(struct sss x,struct sss y){
            if(x.next==y.next){
                return x.id>y.id;
            }
            return x.next>y.next;
        }
    }a[maxn];
    ll n;
    priority_queue<struct sss> q;
    ll b[maxn],c[maxn];
    int main(){
        ll n;
        cin>>n;
        for(int i=1;i<=n;i++){
            a[i].id=i;
            if(i!=n)
                a[i].next=i+1;
            else a[i].next=-1;
        }
        for(int i=1;i<=n;i++){
            cin>>b[i];
            struct sss e;
            e.id=i;
            e.next=b[i];
            q.push(e);
        }
        ll num=0;
        while(q.size()>=2){
            struct sss x,y;
            x=q.top();q.pop();
            y=q.top();q.pop();
            if(a[x.id].next==y.id&&x.next==y.next){
                x.next++;
                a[x.id].next=a[y.id].next;
                q.push(x);
                num++;
            }
            else{
                c[x.id]=x.next;
                q.push(y);
            }
        }
        if(!q.empty()){
            struct sss x=q.top();q.pop();c[x.id]=x.next;
        }
        if(!q.empty()){
            struct sss x=q.top();q.pop();c[x.id]=x.next;
        }
        cout<<n-num<<"
    "; 
        for(int i=1;i<=n;i++){
            if(c[i]!=0){
                cout<<c[i]<<" ";
            }
        }
    } 
    View Code

    CodeForces 455C

    题意:给定N,M和Q,N表示有N个城市,M条已经修好的路,修好的路是不能改变的,然后是Q次操作,操作分为两种,一种是查询城市x所在的联通集合中,最长的路为多长。二是连接两个联通集合,采用联通之后最长路最短的方案,无环图。

    思路:因为一开时的图是不可以改变的,所以一开始用dfs处理出各个联通集合,并且记录住最大值,然后就是Q次操作,用并查集维护,注意因为联通的时候要采用最长路径最短的方案,所以s的转移方程变为s = max(s, (s+1)/2 + (s0+1)/2 + 1),因为两个连通块都记录了树的直径,如果我们其中一条路径是 1-2-3-4-5-6-7,那么这个连通块我肯定选择4点连接,为什么呢,因为如果我选3,那么我就可以走3-4-5-6-7,很明显长度更大,那么最优的肯定是选择中间点,所以我们每次合并的时候就是 选择两个路径的中间点长度再加上新加的那条求一个max

     

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
     
    using namespace std;
    const int maxn = 3 * 1e5 + 5;
    int N, M, Q, f[maxn], s[maxn];
    int root, ans, rec;
    vector<int> g[maxn];
     
    int getfar(int x) {
        return f[x] == x ? x : f[x] = getfar(f[x]);
    }
     
    void link (int u, int v) {
        int x = getfar(u);
        int y = getfar(v);
     
        if (x == y)
            return;
     
        if (s[x] < s[y])
            swap(s[x], s[y]);
     
        f[y] = x;
        s[x] = max(s[x], (s[x] + 1) / 2 + (s[y] + 1) / 2 + 1);
    }
     
    void dfs (int u, int p, int d) {
        f[u] = root;
     
        if (d > ans) {
            ans = d;
            rec = u;
        }
     
        for (int i = 0; i < g[u].size(); i++) {
            if (g[u][i] != p)
                dfs(g[u][i], u, d+1);
        }
    }
     
    int main () {
        int type, u, v;
     
        scanf("%d%d%d", &N, &M, &Q);
        for (int i = 1; i <= N; i++) {
            f[i] = i;
            g[i].clear();
        }
     
        for (int i = 0; i < M; i++) {
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
     
        for (int i = 1; i <= N; i++) {
            if (f[i] == i) {
                root = rec = i;
                ans = -1;
                dfs(i, 0, 0);
     
                ans = -1;
                dfs(rec, 0, 0);
                s[i] = ans;
            }
        }
     
        for (int i = 0; i < Q; i++) {
            scanf("%d", &type);
            if (type == 1) {
                scanf("%d", &u);
                v = getfar(u);
                printf("%d
    ", s[v]);
            } else {
                scanf("%d%d", &u, &v);
                link(u, v);
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    PHP中功能强大却少使用的函数
    php 判断美国zip code
    php 操作 MySQL 中的Blob类型
    THINKPHP 多域名 MEMCACHE方式共享SESSION数据
    phpQuery—基于jQuery的PHP实现
    Error calling method on NPObject!
    国外共享软件热门上载站点
    你留意过自己的父母吗?
    域名LOGO产生器 (全智能)
    想要创业者须知 企业注册商标6大步
  • 原文地址:https://www.cnblogs.com/Lis-/p/11330620.html
Copyright © 2020-2023  润新知