20170903模拟赛
失踪人口回归
这次比赛真心丧,还好有T1,不然早就爆0了。
T1:
卡片(card)
【题目描述】
lrb喜欢玩卡牌。他手上现在有张牌,每张牌的颜色为红绿蓝中的一种。现在他有两种操作。一是可以将两张任意位置的不同色的牌换成一张第三种颜色的牌;二是可以将任意位置的两张相同颜色的牌换成一张该颜色的牌。两个操作后都可以将生成的牌放到任意位置。现在他想知道,最后一张牌可能是什么颜色的。
【输入描述】
第一行输入一个,表示卡牌数量。
第二行输入一个由’B’,’G’,’R’组成的长度为的字符串,分别表示卡牌的颜色为蓝色、绿色、红色中的一种。
【输出描述】
输出’B’,’G’,’R’中的若干个字母,按字典序输出。代表可能的最后一张牌的颜色。
【样例】
输入1 |
输出1 |
2 RB |
G |
输入2 |
输出2 |
3 GRG |
BR |
输入3 |
输出3 |
4 BBBB |
B |
【数据范围】
n<=200
题解:傻逼判断题吧,会if和for就能A吧。
由于第二种操作,所有张数大于二的牌都默认等于2,
(一)如果所有颜色都有,显然可以合成所有颜色;
(二)如果只有一种颜色,显然只有这种;
(三)如果有两种颜色张数大于二,同(一);
(四)如果出现0 1 2的情况,则可以合成0和1两种;
(五)如果出现0 1 1的情况,则可以合成0一种;
应该就这些了吧
代码:
#include<cstdio> #define Fn "card" char s[205]; int main(){ freopen(Fn".in","r",stdin); freopen(Fn".out","w",stdout); int n; bool r=0,g=0,b=0,r2=0,g2=0,b2=0; scanf("%d%s",&n,s+1); for(int i=1;i<=n;i++) switch(s[i]){ case 'R':if(!r)r=1;else if(!r2)r2=1;break; case 'G':if(!g)g=1;else if(!g2)g2=1;break; case 'B':if(!b)b=1;else if(!b2)b2=1; } if(r&&g&&b||(int)(r2)+g2+b2>1){puts("BGR");return 0;} if((int)(r)+g+b==1){if(r)puts("R");if(g)puts("G");if(b)puts("B");return 0;} if(r2&&(g||b)){puts("BG");return 0;} if(g2&&(r||b)){puts("BR");return 0;} if(b2&&(r||g)){puts("GR");return 0;} if(g&&b){puts("R");return 0;} if(g&&r){puts("B");return 0;} if(r&&b){puts("G");return 0;} }
(写的贼丑)
T2:
取数(win)
【题目描述】
lrb目前有个数字,他想知道这个数中选出若干个数,平均数减中位数的最大值是多少。可以证明,对于一个递增数列,如果是平均数减中位数最大时的中位数,表示在两边分别取相邻数字的数量,表示以为中位数,在两侧各取相邻个数时平均数减中位数的值,那么为关于的单峰函数。
【输入描述】
第一行为,为数字个数。
第二行有个数,分别代表个数字。
【输出描述】
输出一个数,为平均数减中位数的最大值,保留两位小数。
【样例】
输入 |
输出 |
4 1 2 3 4 |
0.33 |
题解:
首先取奇数个数显然不会比取偶数个数劣。因为多取一个数导致中位数在中间取平均值会导致中位数更偏向平均数。
其次在确定了中位数后,对于一个最优解的中位数,它向两边拓展时必然是一个单峰函数(题目中已告知),否则会有一个位置取中位数比它更优。所以我们可以直接枚举中位数,对于每个位置三分这个位置的最优解/较优解并更新答案即可。
三分!!!!
看到了单峰函数没想到三分我也是醉了
不了解三分的可以看一下这位神犇的博客:http://blog.csdn.net/pi9nc/article/details/9666627
代码:
#include<cstdio> #include<algorithm> #define mid1 ((rt-lt)/3+lt) #define mid2 (rt-(rt-lt)/3) #define min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) #define r register #define Fn "win" typedef long long ll; typedef double d; using std::sort; double h; int n,a[100005]; ll pre[100005]; inline int read(){ r int x=0,f=1;r char c=getchar(); for(;c<'0'||c>'9';f=c=='-'?-1:1,c=getchar()); for(;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0',c=getchar()); return x*f; } d check(ll w,ll l){ ll sum=pre[w]-pre[w-l-1]+pre[n]-pre[n-l]; return (d)sum/(d)(l*2+1)-(d)a[w]; } int main(){ freopen(Fn".in","r",stdin); freopen(Fn".out","w",stdout); n=read(); if(n<3){puts("0.00");return 0;} for(r int i=1;i<=n;i++)a[i]=read(); sort(a+1,a+1+n); for(r int i=1;i<=n;i++)pre[i]=pre[i-1]+a[i]; for(r int i=1;i<=n;i++){ r ll lt=0,rt=min(i-1,n-i); while(lt<rt) if(check(i,mid1)>check(i,mid2))rt=mid2-1; else lt=mid1+1; h=max(h,check(i,lt)); } printf("%.2lf\n",h); return 0; }
(一写三分就想用宏)
T3:
密码(key)
【题目描述】
lrb去柜员机取钱,并输入了他的四位密码。但是这一切都被一个黑帮拍摄了下来。幸好,这一次他的银行卡里并没有剩余任何钱。lrb知道了他的账户被异常登录后十分害怕,然后修改了他的密码。从此以后他为了不被看出密码,他开始“徐晃”。但这一切还是被拍摄了下来,拍摄多次以后,这个黑帮找到了你,希望你在一秒内帮他算出lrb的可能使用的密码数量。
【输入描述】
第一行输入一个,为lrb被拍摄的动作次数。
接下来行,每行第一个数为,表示lrb接下来的手指移动次数,接下来为一个长度为的数字串,表示lrb手指经过的轨迹。lrb手指经过的位置,可能没有按,也有可能按了多次。
【输出描述】
输出一行,为可能的密码数量。
【样例】
输入 |
输出 |
2 3 123 3 234 |
5 |
解释 |
|
lrb可能用的是2222,2223,2233,2333,3333五种密码 |
题解:
(话说学长们就是这样互D的吗)
首先可以在O(10L)的时间内预处理出对于每一位向后的第一个某个数字的位置。然后就可以O(10000)暴力dfs每种密码后用O(n)的时间进行check,最后统计进答案即可。有一个加快程序的小技巧是在暴力dfs每种密码时不枚举那些相邻两位数相同的密码,而是将它们表示为权值直接加入答案。另外这题的空间比较紧张,如果比较虚的可以像我一样动态开点:
char *s; s=new char[len+5]; scanf("%s",s+1);
不过指针这东西真别多用,用不好后果很惨重。
代码:
#include<cstdio> #include<cstring> #define r register #define Fn "key" typedef unsigned short us; const us temp[5]={0,1,3,3,1}; int n,ans; us l[1005]; char *s[1005]; char a[5]; struct AriM{ us last[10]; void init(){memset(last,0,10*sizeof(us));} }*suf[1005]; inline us read(){ r us x=0;r short f=1;r char c=getchar(); for(;c<'0'||c>'9';f=c=='-'?-1:1,c=getchar()); for(;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0',c=getchar()); return x*f; } inline bool check(int st){ int cnt,h; for(r int i=1;i<=n;i++){ for(cnt=1,h=0;cnt<=st;cnt++) if(suf[i][h].last[a[cnt]])h=suf[i][h].last[a[cnt]];else break; if(cnt<=st)return 0; } return 1; } inline void dfs(int st){ for(r char i=0;i<10;i++) if(i!=a[st-1]){ a[st]=i; if(check(st))ans+=temp[st]; if(st<4)dfs(st+1); } } int main(){ freopen(Fn".in","r",stdin); freopen(Fn".out","w",stdout); n=read(); for(r int i=1;i<=n;i++){ l[i]=read(); s[i]=new char[l[i]+5]; suf[i]=new AriM[l[i]+5]; scanf("%s",s[i]+1); r AriM t;t.init(); for(r int j=l[i];j>=0;j--){ suf[i][j]=t; if(j)t.last[s[i][j]-'0']=j; } } a[0]=-1; dfs(1); printf("%d\n",ans); return 0; }
T4:
三角之恋(tri)
【题目描述】
给出平面上的个等腰直角三角形。每个三角形用个整数描述。一个三角形的3个顶点分别是,和。
你的任务是计算这些三角形覆盖的总面积。
【输入描述】
第一行为,为三角形个数。
接下来每行三个数,用于描述一个三角形。
【输出描述】
输出一个数,为这些三角形覆盖的总面积,保留一位小数。
【样例】
输入 |
输出 |
5 -5 -3 6 -1 -2 3 0 0 2 -2 2 1 -4 -1 2 |
24.5 |
题解:
解决这题需要离散化并用扫描线。
首先需要离散每个三角形的(x,y),(x+m,y)(x,y+m)三个点,以便于后面计算答案。
然后我们将每个三角形记做一个只有底部线段,并且右端点不断向缩小的线。在更新时直接暴力排序每个线段并踢出会被某个其他线段覆盖的线段,然后将y坐标不断往上推,在过程中暴力计算每个离散长方形的面积,如果某个长方形没被完全覆盖,那么就减去没覆盖的部分的面积。这样就可以在的时间复杂度内算出答案。
扫描线真的难打,打了一个小时,看来熟练度还是不够啊!
代码:
#include<bits/stdc++.h> #define lb(x,y,z) (lower_bound(x+1,x+y+1,z)-x) #define r register #define Fn "tri" using namespace std; typedef long long ll; struct AriM{ int x,y,x2,m,tx,ty; }l[5005],s[10005]; int n,cntx,cnty,cnts,th; int X[10005],Y[10005]; ll ans; bool used[10005]; inline int read(){ r int x=0,f=1;r char c=getchar(); for(;c<'0'||c>'9';f=c=='-'?-1:1,c=getchar()); for(;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0',c=getchar()); return x*f; } bool cmp(const AriM &a,const AriM &b){ if(a.y!=b.y)return a.y<b.y; if(a.x!=b.x)return a.x<b.x; return a.tx<b.tx; } void solve(){ int t=Y[th+1]-Y[th],rt; for(r int i=s[1].x,j=1;X[i]<=s[cnts].x2;i++){ while(j<cnts&&i>=s[j+1].x)j++; if(X[i]>=s[j].x2)continue; rt=min(X[i+1],s[j].x2); ans+=2ll*(rt-X[i])*t; if(rt>s[j].x2-t) if(X[i]<s[j].x2-t)ans-=(ll)(rt-s[j].x2+t)*(rt-s[j].x2+t); else ans-=(ll)(rt-X[i])*(rt-X[i])+2ll*(X[i]-s[j].x2+t)*(rt-X[i]); } th++; for(r int i=1;i<=cnts;i++) if(s[i].ty<th)used[i]=0; else used[i]=1,s[i].x2=X[s[i].tx]-Y[th]+Y[s[i].y]; r int lt=cnts;cnts=0; for(r int i=1;i<=lt;i++) if(used[i])s[++cnts]=s[i]; } int main(){ freopen(Fn".in","r",stdin); freopen(Fn".out","w",stdout); n=read(); for(r int i=1;i<=n;i++){ l[i].x=read();l[i].y=read();l[i].m=read(); X[++cntx]=l[i].x;X[++cntx]=l[i].x+l[i].m; Y[++cnty]=l[i].y;Y[++cnty]=l[i].y+l[i].m; } sort(X+1,X+cntx+1); sort(Y+1,Y+cnty+1); cntx=unique(X+1,X+cntx+1)-X-1; cnty=unique(Y+1,Y+cnty+1)-Y-1; X[cntx+1]=Y[cnty+1]=2e9; for(r int i=1;i<=n;i++){ l[i].tx=lb(X,cntx,l[i].x+l[i].m); l[i].ty=lb(Y,cnty,l[i].y+l[i].m); l[i].x=lb(X,cntx,l[i].x); l[i].y=lb(Y,cnty,l[i].y); l[i].x2=X[l[i].tx]; } sort(l+1,l+n+1,cmp); l[n+1].y=cnty+1; s[cnts=1]=l[1];th=l[1].y;s[0].x2=-2e9; for(r int i=2;i<n+2;i++){ while(cnts&&l[i].y>th)solve(); th=max(th,l[i].y); r bool f=0; for(r int j=1;j<=cnts;j++) if(l[i].x<s[j].x){ if(l[i].x2<=s[j-1].x2)break; for(r int k=cnts;k>=j;k--)s[k]=s[k-1]; s[j]=l[i]; r int lt=cnts;cnts=j; for(r int k=j+1;k<=lt;k++) if(s[k].x2>s[j].x2)s[++cnts]=s[k]; f=1; } if(!f&&l[i].x2>s[cnts].x2)s[++cnts]=l[i]; } printf("%I64d.%c\n",ans/2,ans&1?'5':'0'); return 0; }