• Codeforces Round #216 (Div. 2)


    以后争取补题不看别人代码,只看思路,今天就是只看思路补完的题,有点小激动。

    A. Valera and Plates

    水题,贪心地先放完第一种食物,在考虑第二种。

    居然被卡了一会,心态要蹦 :(;

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,k;
    int main()
    {
        cin>>n>>m>>k;
        int cnt1=0,cnt2=0;
        for(int i=1;i<=n;i++)
        {
            int g; scanf("%d",&g);
            if(g==1) cnt1++;
            else cnt2++;
        }
        int ans=0;
        if(m<=cnt1) ans+=cnt1-m,m=0;
        else k+=(m-cnt1);
        ans+=max(0,cnt2-k);
        cout<<ans<<endl;
    }
    View Code

    B. Valera and Contest

    题目大意:给你n个数,你不知道第i个是多少,但是你知道里面数的最大值和最小值,所有数的和,

    以及前k大个数的和,让你构造出这n个数。

    思路:按平均数来构造,有余数补上,有一点wa了一次,取膜的时候没有考虑k==n的情况,用0取了膜,

    以后不能犯这个错误了!!!

    #include<bits/stdc++.h>
    using namespace std;
    int n,k,l,r,sa,sk;
    int ans[10005];
    int main()
    {
        cin>>n>>k>>l>>r>>sa>>sk;
        int g=sk/k,sg=sk%k;
        int now=0;
        for(int i=1;i<=k;i++)
        {
            ans[i]=g;
            if(i<=sg) ans[i]++;
            now+=ans[i];
        }
        if(k<n)
        {
            int ssum=sa-sk;
            g=ssum/(n-k),sg=ssum%(n-k);
            for(int i=1;i<=n-k;i++)
            {
                ans[i+k]=g;
                if(i<=sg) ans[i+k]++;
            }
        }
    
        for(int i=1;i<=n;i++) printf("%d%c",ans[i],i==n? '
    ':' ');
        return 0;
    }
    View Code

    C. Valera and Elections

    题目大意:有一棵树,有些边有问题,需要修复,修复的方法就是选出节点的子集,每个选中的节点

    修复这个节点到 1 节点的所有有问题的边,问你最少需要选几个节点。

    思路:还是比较好想的,直接从1开始dfs,用数组ans保存答案,top为其栈顶,每次dfs到一个节点,

    先记录当前栈顶的值,回溯回来后,看当前栈顶是不是和第一次进入的时候一样,如果一样说明,

    这个节点的下面没有有问题的边,如果这个节点通向其父节点的边是有问题的那么这个节点一定要选,

    如果当前栈顶和第一次进入的时候不一样,说明这个节点的子节点中已经有一个作为答案了,

    当前节点不需要选。

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define pb push_back
    #define mk make_pair
    #define fi first
    #define se second
    using namespace std;
    const int N=1e5+5;
    vector<pii> e[N];
    int n,tot=0,ans[N];
    void dfs(int u,int p,int pe)
    {
        int now=tot;
        for(int i=0;i<e[u].size();i++)
        {
            pii to=e[u][i];
            if(to.fi==p) continue;
            dfs(to.fi,u,to.se);
        }
        if(pe==2 && tot==now) ans[++tot]=u;
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<n;i++)
        {
            int from,to,op;scanf("%d%d%d",&from,&to,&op);
            e[from].pb(mk(to,op));
            e[to].pb(mk(from,op));
        }
        dfs(1,-1,-1);
        cout<<tot<<endl;
        for(int i=1;i<=tot;i++) printf("%d%c",ans[i],i==tot?'
    ':' ');
        return 0;
    }
    View Code

    D. Valera and Fools

    题目大意:有n个笨蛋站在一排,每个人手里都有枪,他们的编号分别为1->n,每个人都有一个打死人

    的概率(0-100)%,现在进行k轮游戏,每一轮,除编号最小的人朝编号第二小的人开枪,其他人都向

    编号最小的人开枪,问你k轮之中有几种不同的情形,即活着的人不同的情况。

    QAQ 不会写啊,知道是dp也不会写,看来我dp还是太弱了,该写写dp的专题了!!!

    思路:我们首先要明确一点,第三大编号即以上的人一定是活着的,那么我们用dp[ i ][ j ],表示到达

    最小编号为i 第二小编号为 j 的情形 最少需要几轮游戏。初始状态 dp[ 1 ][ 2 ]=0,这样就能写出状态

    转移方程了。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3005;
    const int inf=0x3f3f3f3f;
    int n,k,dp[N][N],p[N],mx[N];//mx[i] 表示i及以后概率的最大值。
    int main()
    {
        cin>>n>>k;
        for(int i=1;i<=n;i++) scanf("%d",&p[i]);
        for(int i=n;i>=1;i--) mx[i]=max(mx[i+1],p[i]);
        memset(dp,inf,sizeof(dp));
        dp[1][2]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(dp[i][j]>=inf) continue;
                if(mx[j]>0 && p[i]<100) dp[j][j+1]=min(dp[j][j+1],dp[i][j]+1);
                if(p[i]>0 && mx[j]!=100) dp[i][j+1]=min(dp[i][j+1],dp[i][j]+1);
                if(mx[j]>0 && p[i]>0) dp[j+1][j+2]=min(dp[j+1][j+2],dp[i][j]+1);
            }
        }
        int ans=0;
        for(int i=1;i<=n+2;i++)//i,j如果大于n说明 只剩一个人或者没有人或者。
        {
            for(int j=i+1;j<=n+2;j++)
            {
                if(dp[i][j]<=k) ans++;
    
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    View Code


    E. Valera and Queries

    题目大意:给你n条线段,然后又m组询问,每一组询问有cnt[ i ]个数,问你这些线段中有多少是

    覆盖了这里面任意一个点的,覆盖1个2个...都可以。

    一看就知道是线段树(树状数组)的题目,可是不会写啊!!

    思路:我们可以很巧妙地转换一下,求覆盖的线段,可以先求一个点都没有覆盖的线段,然后总的

    减去就好了,这样问题就变成了,给你的cnt[ i ]个点相邻的两个之间有多少条线段。这样我们就能用

    树状数组了,先将线段按 r 值排序,注意如果是保存前缀,一定是要用 r 值排序的。每个树状数组的

    节点保存,所管理的区域范围内的 l 的值,并将 l 值排好序。找位于相邻两点之间的线段数量时,

    树状数组每个节点lower_bound一下就好了。

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    using namespace std;
    const int N=3*1e5+5;
    int n,m,top=1,x[N];//x 保存r的值,离散化的时候去重,排序。
    vector<int> bt[N],o[N];
    pii p[N];
    bin_search(int tar)// 离散化,r真正地值查找对应的编号。
    {
        int l=0,r=top;
        while(1)
        {
            int mid=(l+r)>>1;
            if(x[mid]==tar) return mid;
            else if(x[mid]<tar) l=mid+1;
            else r=mid-1;
        }
    }
    int low_bit(int u){return u&-u;}
    void build()
    {
        for(int i=1;i<top;i++)
        {
            int up=low_bit(i);
            for(int j=i;j>i-up;j--)
            {
                for(int k=0;k<o[j].size();k++)
                {
                    int now=o[j][k];
                    bt[i].pb(now);
                }
            }
            sort(bt[i].begin(),bt[i].end());
        }
    }
    int query(int u,int l)
    {
        int sum=0;
        while(u>0)
        {
            int c=upper_bound(bt[u].begin(),bt[u].end(),l)-bt[u].begin();
            sum+=bt[u].size()-c;
            u-=low_bit(u);
        }
        return sum;
    }
    int main()
    {
        cin>>n>>m;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&p[i].fi,&p[i].se);
            x[top++]=p[i].se;
        }
        sort(x,x+top);
        top=unique(x,x+top)-x;
        for(int i=0;i<n;i++)
        {
            int g=bin_search(p[i].se);
            o[g].pb(p[i].fi);
        }
        build();
        int sum=0,q,p;
        while(m--)
        {
            sum=0;p=0;
            int cnt; scanf("%d",&cnt);
            for(int i=1;i<=cnt;i++)
            {
                scanf("%d",&q);
                int pos=lower_bound(x+1,x+top,q)-x;
                pos--;
                sum+=query(pos,p);
                p=q;
            }
            sum+=query(top-1,p);
            printf("%d
    ",n-sum);
        }
        return 0;
    }
    View Code

     ps: e题网上的作法貌似比我的快,用的也是树状数组,不过他是把题目原来的线段和后来两点间的

    线段保存在一起,最后一起离线处理,把所有线段都按 l  的大小降序(如果 l 一样,r小的优先),这样

    从头往后扫,这样就保证了后面的线段的 l 值不会大于前面的,这样只要看 r 值就行了,r的个数

    保存在树状数组中。

  • 相关阅读:
    设计模式(二十)---迭代器模式
    设计模式(十九)---观察者模式
    设计模式(十八)---模板方法模式
    设计模式(十七)---策略模式
    ElasticSearch 安装
    MongoDB进击 Linux单机安装
    List集合去除重复对象。。。记录一下
    Springboot整合mybatisPlus实现分页
    git记录
    Springboot异常处理errorController
  • 原文地址:https://www.cnblogs.com/CJLHY/p/7281327.html
Copyright © 2020-2023  润新知