hgoi#20190508

muronglin
muronglin 2019年05月08日
  • 在其它设备中阅读本文章

T1-Bachgold Problem

给你一个整数 n(2≤n≤100000),问最多能将其分解成多少质数的和。 在第一行输出最多的质数数量 k,下一行输出 k 个整数,为这些质数。

解法

贪心,分解成 2 和 3 明显是最优的

ac 代码

#include<bits/stdc++.h>
using namespace std;
int n;
int main()
{
    scanf("%d",&n),printf("%dn",n/2);
    while(n>3)printf("%d ",2),n-=2;
    printf("%dn",n);
    return 0;
}

T2-Parallelogram is Back

输入一个平行四边形的三个顶点,输出第四个点的可能位置数量和这些位置

解法

这就是简单数学推导了,初中的知识吧 =-= 第四个点一定只有 3 种可能,都算出来就好了

ac 代码

#include<bits/stdc++.h>
using namespace std;
int a[5],b[5],suma,sumb;
int main()
{
    for(int i=1;i<=3;i++)scanf("%d%d",&a[i],&b[i]),suma+=a[i],sumb+=b[i];
    puts("3");
    for(int i=1;i<=3;i++)printf("%d %dn",suma-2a[i],sumb-2b[i]);
    return 0;
}

T3-Voting

给定一个字符串,字符串中字符为 D 或 R,代表两个团队 从 1 开始,每个人都有发言的权利,发言时,可以禁言一个人,使那个人以后都不能发言 如果一圈发言完还有 1 个以上的人能发言,就从 1 重新开始,直到只有 1 个人能发言,那个人所在的团队获胜

解法

可以挂个链表,然后头尾连起来,可以一直扫,结束条件可以优化,当场上只有一个团队时结束 因为最优策略是将另一个团队后一个发言的人禁言,所以每次扫到一个人,看是否被禁言,如果被禁言了,在链表中删去这个点 这样做的复杂度是 nlogn 的,因为每次会禁言一半

ac 代码

#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int n,nw,s[200010],l[200010],r[200010],cnta,cntb,lena,lenb,dela,delb;
char ch;
int main()
{
    scanf("%d",&n),getchar();
    for(register int i=1;i<=n;++i)
    {
        scanf("%c",&ch),l[i]=i-1,r[i]=i+1;
        if(ch=='D')s[i]=1,cnta++;else s[i]=2,cntb++;
    }
    l[1]=n,r[n]=1,nw=1;
    while(cnta!=lena&&cntb!=lenb)
    {
        if(s[nw]==1)
        {
            if(dela)r[l[nw]]=r[nw],l[r[nw]]=l[nw],dela--;
            else delb++,lenb++;
        }
        if(s[nw]==2)
        {
            if(delb)r[l[nw]]=r[nw],l[r[nw]]=l[nw],delb--;
            else dela++,lena++;
        }
        nw=r[nw];
    }
    if(cnta==lena)puts("R");else puts("D");
    return 0;
}

T4-Leaving Auction

一场拍卖会,共 n 个买家。这些买家共出价 n 次,有的买家可能一次都没有出价。 每次出价用(ai,bi)表示,ai 为此次出价人的编号,bi 为价格。 出价严格递增(bi<bi+1)并且没有玩家在一轮竞拍中在没有其他竞争对手的情况下自动增加自己的出价(ai!=ai+1)。 现在给定 q 次查询,每次去掉一些出价者及其所有出价,问最后谁是赢家并且他以什么价格赢得拍卖品。

解法

首先第一小问,求谁是赢家,可以先排序,由于 k 的和限定在 20w,所以每次从排名上扫来找到赢家 对于第二小问,求赢家出的价格,可以换一种思路,找到比第二名的最高出价高的第一个出价,就是答案 这个就很好维护了,找到第二名的复杂度也是 k 的和,然后二分找到比他大的第一个出价就可以了 总复杂度 nlogn,tips:memset 的复杂度是 n(emmmm),所以不要在循环里加上 memset,怎么死的都不知道

ac 代码

#include<bits/stdc++.h>
using namespace std;
struct node{int num;}b[200010];
vector<int>a[200010];
int n,m,q,x,y,lim[200010],ll[200010];
int cmp(node xx,node yy){return axx.num.size()-1]<ayy.num.size()-1];}
int find(int k)
{
    for(int i=k;i>=1;i--)if(!lim[b[i].num])return i;
    return 0;
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<=n;i++)a[i].push_back(0);
    for(int i=1;i<=n;i++)scanf("%d%d",&x,&y),a[x].push_back(y),b[i].num=i;
    sort(b+1,b+1+n,cmp),scanf("%d",&m);
    while(m--)
    {
        scanf("%d",&q);
        for(int i=1;i<=q;i++)scanf("%d",&ll[i]),lim[ll[i]]=1;
        x=find(n),lim[b[x].num]=1,y=find(x-1),lim[b[x].num]=0;
        if(!a[b[x].num][a[b[x].num].size()-1])puts("0 0");
        else printf("%d %dn",b[x].num,*upper_bound(a[b[x].num].begin(),a[b[x].num].end(),a[b[y].num][a[b[y].num].size()-1]));
        for(int i=1;i<=q;i++)lim[ll[i]]=0;
    }
    return 0;
}