【20170903】模拟赛

卡片(card)

【题目描述】

       lrb喜欢玩卡牌。他手上现在有n张牌,每张牌的颜色为红绿蓝中的一种。现在他有两种操作。一是可以将两张任意位置的不同色的牌换成一张第三种颜色的牌;二是可以将任意位置的两张相同颜色的牌换成一张该颜色的牌。两个操作后都可以将生成的牌放到任意位置。现在他想知道,最后一张牌可能是什么颜色的。

【输入描述】

       第一行输入一个,表示卡牌数量。

       第二行输入一个由’B’,’G’,’R’组成的长度为n的字符串,分别表示卡牌的颜色为蓝色、绿色、红色中的一种。

【输出描述】

       输出’B’,’G’,’R’中的若干个字母,按字典序输出。代表可能的最后一张牌的颜色。

【样例】

输入1

输出1

2

RB

G

输入2

输出2

3

GRG

BR

输入3

输出3

4

BBBB

B

【数据范围】

对于100%的数据,n<=200

这是一道很简单的判断题。。。。。。主要就是根据每个字母出现的次数进行判断。

#include<iostream>
#include<cstdio>
using namespace std;
int n,num[4],a,x,y;
char c,d;
bool ans[4];
int main(){
    freopen("card.in","r",stdin);
    freopen("card.out","w",stdout);
    scanf("%d",&n);
    if(n==0) return 0;
    for(int i=1;i<=n;i++){
        cin>>c;
        if(c=='B') {a+=num[1]==0;num[1]++;d='B';}
        if(c=='G') {a+=num[2]==0;num[2]++;d='G';}
        if(c=='R') {a+=num[3]==0;num[3]++;d='R';}
    }
    if(a==3) {printf("BGR
");return 0;}
    if(a==1) {cout<<d<<endl;return 0;}
    for(int i=1;i<=3;i++){
        if(num[i]) x==0?x=i:y=i;
        if(!num[i]) ans[i]=1;
    }
    if(num[x]>1&&num[y]==1) ans[y]=1;
    if(num[x]==1&&num[y]>1) ans[x]=1;
    if(num[x]>1&&num[y]>1) ans[x]=ans[y]=1;
    if(ans[1]) printf("B");
    if(ans[2]) printf("G");
    if(ans[3]) printf("R");
    puts("");
    return 0;
}

取数(win)

【题目描述】

       lrb目前有n个数字,他想知道这n个数中选出若干个数,平均数减中位数的最大值是多少。可以证明,对于一个递增数列a,如果是平均数aw减中位数最大时的中位数,l表示在w两边分别取相邻数字的数量,f(w,l)表示以aw为中位数,在w两侧各取相邻l个数时平均数减中位数的值,那么f(w,l)为关于l的单峰函数。

【输入描述】

第一行为n,为数字个数。

第二行有n个数,分别代表n个数字。

【输出描述】

       输出一个数,为平均数减中位数的最大值,保留两位小数。

【样例】

输入

输出

4

1 2 3 4

0.33

【数据范围】

对于60%数据,n<=21

对于75%的数据,n<=2000

对于100%的数据,n<=10^5,0<=ai<=10^6

根据题目显而易见,只可能取奇数个,不然aw不存在

之前是yy了一个证法的样子,可是好像没什么正确性。

反正一定是奇数个。

为了使平均数尽可能大,我们要取得数是:

因为是个单峰函数,我们可以用三分法来求它的峰值。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define MN 200000
#define ll long long
using namespace std;
int n,a[MN+5];
ll pre[MN+5];
int l,r,mid1,mid2,ansi,ansl;
double ansv,v1,v2;
double f(int i,int l){
    ll sum=0;
    sum+=pre[i]-pre[i-l-1];
    sum+=pre[n]-pre[n-l];
    return (double)sum/(double)(2*l+1)-(double)a[i];
}
int main(){
    freopen("win.in","r",stdin);
    freopen("win.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]); pre[i]=pre[i-1]+a[i];
    }
    sort(a+1,a+n+1);
    if(n<=2) {printf("0.00
");return 0;}  //特判 
    for(int i=1;i<=n;i++){   //枚举中位数 
        l=0; r=min(i-1,n-i);
        while(l+1<=r){  //三分 
            mid1=l+(r-l)/3; mid2=r-(r-l)/3;
            v1=f(i,mid1); v2=f(i,mid2);
            if(v1>v2) r=mid2-1; else l=mid1+1;
        }
        double v=f(i,l);  //更新 
        if(v>ansv){ansv=v; ansi=i; ansl=l;}
    }
    printf("%.2lf
",ansv);
    return 0;
}

密码(key)

【题目描述】

       lrb去柜员机取钱,并输入了他的四位密码。但是这一切都被一个黑帮拍摄了下来。幸好,这一次他的银行卡里并没有剩余任何钱。lrb知道了他的账户被异常登录后十分害怕,然后修改了他的密码。从此以后他为了不被看出密码,他开始“徐晃”。但这一切还是被拍摄了下来,拍摄多次以后,这个黑帮找到了你,希望你在一秒内帮他算出lrb的可能使用的密码数量。

【输入描述】

       第一行输入一个n,为lrb被拍摄的动作次数。

       接下来n行,每行第一个数为t,表示lrb接下来的手指移动次数,接下来为一个长度为t的数字串,表示lrb手指经过的轨迹。lrb手指经过的位置,可能没有按,也有可能按了多次。

【输出描述】

       输出一行,为可能的密码数量。

【样例】

输入

输出

2

3 123

3 234

5

解释

lrb可能用的是2222,2223,2233,2333,3333五种密码

【数据范围】

设所有轨迹串的总长度为L。

对于35%的数据,L<=5000

对于100%的数据,1<=n<=1000,1<=t<=10^4,L<=10^6

把所有的串拼在一起,可以在O(10L)的时间内预处理出对于每一位向后的第一个某个数字的位置。

用f[i][j]表示从第i个位置(包括i(因为可以多次按同一个键))向后查找j数(0~9)的位置,不存在设为1000000,再令f[1000000][0~9]=10000000

然后就可以暴力枚举每种密码后用的O(n)时间进行check,从第0号位置开始。用f[0][第一位的数]跳到下一个位置,接着用f[刚刚跳到的位置][第二位的数]继续跳,以此类推。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char s[1000005];
int n,t[1005],f[1000005][10];
int main(){
    freopen("key.in","r",stdin);
    freopen("key.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%s",&t[i],s+t[i-1]);
        t[i]+=t[i-1];
    }
    for(int i=0;i<10;i++) f[t[n]][i]=t[n];
    for(int i=t[n]-1;i>=0;i--){
        memcpy(f[i],f[i+1],sizeof f[i+1]);
        f[i][s[i]-'0']=i;
    }
    int x,ans=0;
    for(int a=0;a<10;a++) for(int b=0;b<10;b++)
    for(int c=0;c<10;c++) for(int d=0;d<10;d++){
        x=0;
        for(int i=1;i<=n;i++)
        if(f[f[f[f[t[i-1]][a]][b]][c]][d]<t[i]) x++;
        ans+=x/n;
    }
    printf("%d",ans);
    return 0;
}

三角之恋(tri)
【题目描述】
给出平面上的
n个等腰直角三角形。每个三角形用3个整数描述x, y, m(m > 0)。一个三角
形的 3个顶点分别是
(x, y),(x + m, y)(x, y + m)
你的任务是计算这些三角形覆盖的总面积。

【数据范围】

对于100%的数据,n<=5000,|xi,yi|<=5*10^8,1<=mi<=10^9

解决这题需要离散化并用扫描线。

首先需要离散每个三角形的(x,y),(x+m,y)(x,y+m)三个点,以便于后面计算答案。

然后我们将每个三角形记做一个只有底部线段,并且右端点不断向缩小的线。在更新时直接暴力排序每个线段并踢出会被某个其他线段覆盖的线段,然后将y坐标不断往上推,在过程中暴力计算每个离散长方形的面积,如果某个长方形没被完全覆盖,那么就减去没覆盖的部分的面积。这样就可以在O(n^2)的时间复杂度内算出答案。

嗯没有时间了,只能贴标程了。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
struct sth
{
    int x,y,x2,m,tx,ty;
}l[5005]; 
    int n,lx,ly,ls,th;
    int X[10005],Y[10005];
    sth S[10005];
    bool u[10005];
    ll ans;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
bool cmp(sth l1,sth l2)
{
    if (l1.y!=l2.y) return l1.y<l2.y;
    if (l1.x!=l2.x) return l1.x<l2.x;
    return l1.tx<l2.tx;
}
void cal()
{
    int to=Y[th+1]-Y[th],ri;
    for (int i=S[1].x,j=1;X[i]<=S[ls].x2;i++)
    {
        while (j<ls&&i>=S[j+1].x) j++;
        if (X[i]>=S[j].x2) continue;
        ri=min(X[i+1],S[j].x2);
        ans+=2LL*(ri-X[i])*to;
        if (ri>S[j].x2-to)
        {
            if (X[i]<S[j].x2-to)
            {
                int l=ri-(S[j].x2-to);
                ans-=(ll)l*l;
            }
            else
            {
                ans-=(ll)(ri-X[i])*(ri-X[i]);
                int l=X[i]-(S[j].x2-to);
                ans-=2LL*l*(ri-X[i]);
            }
        }
    }
    th++;
    for (int i=1;i<=ls;i++)
        if (S[i].ty<=th) u[i]=false; 
        else
        {
            u[i]=true;
            S[i].x2=X[S[i].tx]-(Y[th]-Y[S[i].y]);
        }
    int L=ls;ls=0;
    for (int i=1;i<=L;i++)
        if (u[i]) S[++ls]=S[i];
}
int main()
{
    freopen("tri.in","r",stdin);
    freopen("tri.out","w",stdout); 
    n=read();
    for (int i=1;i<=n;i++)
    {
        l[i].x=read(),l[i].y=read(),l[i].m=read();
        X[++lx]=l[i].x,X[++lx]=l[i].x+l[i].m;
        Y[++ly]=l[i].y,Y[++ly]=l[i].y+l[i].m;
    }
    sort(X+1,X+lx+1);
    sort(Y+1,Y+ly+1);
    lx=unique(X+1,X+lx+1)-X-1;
    ly=unique(Y+1,Y+ly+1)-Y-1;
    Y[ly+1]=2e9;X[lx+1]=2e9;
    for (int i=1;i<=n;i++)
    {
        l[i].tx=lower_bound(X+1,X+lx+1,l[i].x+l[i].m)-X;
        l[i].ty=lower_bound(Y+1,Y+ly+1,l[i].y+l[i].m)-Y;
        l[i].x=lower_bound(X+1,X+lx+1,l[i].x)-X;
        l[i].y=lower_bound(Y+1,Y+ly+1,l[i].y)-Y;
        l[i].x2=X[l[i].tx];
    }
    sort(l+1,l+n+1,cmp);
    l[n+1].y=ly+1;
    S[ls=1]=l[1];th=l[1].y;S[0].x2=-2e9;
    for (int i=2;i<=n+1;i++)
    {
        while (ls&&l[i].y>th)
            cal();
        if (th<l[i].y) th=l[i].y;
        bool found=false;
        for (int j=1;j<=ls;j++)
            if (l[i].x<S[j].x)
            {
                if (l[i].x2<=S[j-1].x2) break;
                for (int k=ls;k>=j;k--) S[k+1]=S[k];
                S[j]=l[i];int L=ls+1;ls=j;
                for (int k=j+1;k<=L;k++)
                    if (S[k].x2>S[j].x2) S[++ls]=S[k];
                found=true;
            }
        if (!found&&l[i].x2>S[ls].x2) S[++ls]=l[i];
    }
    if (ans&1) printf("%I64d.5",ans/2);
    else printf("%I64d.0",ans/2);
    return 0;
}

来自PaperCloud的博客,未经允许,请勿转载,谢谢。

原文地址:https://www.cnblogs.com/PaperCloud/p/7500518.html