NOIP2012复赛提高组day1(A:Vigenère 密码 B:国王游戏 C:开车旅行)
来源:互联网 发布:安捷伦gpc软件 编辑:程序博客网 时间:2024/05/13 19:58
A题:
在xiaoC创建的题库中有这道题,
我们都已经做过了
但是就算没做过
第一题是不可能会WA的
这题十分的简单
我们通过仔细观察会发现
密文=明文+密钥-‘A’/’a’;
(我们假定’Z’+1=’A’,’z’+1=’a’)
于是乎这题就被完美的解决了。。
#include<bits/stdc++.h>using namespace std;#define M 1005char Key[M],str[M];int main(){ scanf("%s %s",Key,str); int n=strlen(str),m=strlen(Key); for(int i=0;i<n;i++){ int j=i%m; if(str[i]>='A'&&str[i]<='Z'&&Key[j]>='a'&&Key[j]<='z')Key[j]-=32; else if(str[i]>='a'&&str[i]<='z'&&Key[j]>='A'&&Key[j]<='Z')Key[j]+=32; if(str[i]>='a'&&str[i]<='z'){ str[i]-=Key[j]-'a'; if(str[i]<'a')str[i]+=26; }else if(str[i]>='A'&&str[i]<='Z'){ str[i]-=Key[j]-'A'; if(str[i]<'A')str[i]+=26; } }puts(str); return 0;}
B题:
这是有史以来最骚的联赛第二题
别的题最多就sort+贪心就结束了
这题偏要来个高精
还是个最冷门的高精除
不过幸好是高精除低精,还是比较容易写的
然而。。。
蒟蒻的博主连怎样排序都不会。。
傻乎乎的用a/b排序,最后完全过不了样例
于是博主要在此推销两法宝:
rand水分大法以及多排序叠加水分大法
多排序叠加水分大法是1113大神最先开发并使用的
在某一道但是我们整个机房都没几个人想到正解的贪心题中
这位大神足足敲(复制粘贴0.0)了20+种排序算法取最小值作为解,水到了60分,
而这道B题他也水到了60分(这次只用了7种)
于是他获得了一个封号:水分大王
而rand水分大法不知是哪位大神发明的
现在的应用范围十分普遍
使用随机数随便交换两个大臣,经过较多次数变换后取最小的值
蒟蒻博主用这种方法水到了60分
隔壁的jiedai大神40%rand,剩下是无高精的正解写法,最终50分(大神一不小心手抖了一下)
叫上rand水分大法的代码(多排序叠加有点长,不敢发。。):
//PS:这是rand水分大法的代码,要复制正解的同志别复制错了#include<bits/stdc++.h>using namespace std;#define M 1005#define LL long longint n;struct node{ int a,b;}s[M];int check(){ LL tmp=s[0].a,ans=0; for(int i=1;i<=n;i++){ LL res=tmp/s[i].b; tmp*=s[i].a; if(res>ans)ans=res; }return ans;}int main(){ srand(time(NULL)); scanf("%d",&n); scanf("%d %d",&s[0].a,&s[0].b); for(int i=1;i<=n;i++)scanf("%d %d",&s[i].a,&s[i].b); LL ans=check(); for(int i=1;i<=40000;i++){//rand次数可以在调大一点点,但是不能太大,会TLE的(1e5->50分) int a,b; do { a=rand()%n+1; b=rand()%n+1; }while(a==b); node tmp=s[a]; s[a]=s[b];s[b]=tmp; LL res=check(); if(res>0&&res<ans)ans=res; }printf("%d\n",ans); return 0;}
而后我们就可以来研究一下正解了
首先我们知道,若是最终答案
为
那么根据
为什么呢?
对于某一个x
我们可以假设后面的所有数都已经确定
即前x个数只是需要交换顺序
于是这个时候
那么想让当前的值最小,必然是
以此类推即可
但是我们知道这一题最终的答案并不是这个形式
而是
那么怎么办呢
因为
而对于
而后用
于是值为
当然,
众大神们还需要敲一个高精乘低精和高精除低精的模板
代码如下:
#include<bits/stdc++.h>using namespace std;#define M 1005#define W 10000#define P 100007#define LL long longstruct Bigint{ int num,val[M]; Bigint operator *(const int &A)const{//高精乘 Bigint res;res.num=num; for(int i=1;i<=num;i++)res.val[i]=val[i]*A; for(int i=1;i<=num;i++) if(res.val[i]>=W){ res.val[i+1]+=res.val[i]/W; res.val[i]%=W; } if(res.val[res.num+1])res.num++; return res; } Bigint operator /(const int &A)const{//高精除 Bigint res;res.num=num;int tmp=val[num]; for(int i=num;i>=1;i--){ res.val[i]=tmp/A; tmp=tmp%A*W+val[i-1]; }if(!res.val[num])res.num--; return res; } bool operator <(const Bigint &A)const{//比较 if(num!=A.num)return num<A.num; for(int i=num;i>=1;i--) if(val[i]!=A.val[i])return val[i]<A.val[i]; return false; }}ans[M],ANS;struct node{ int a,b,k; bool operator <(const node &A)const{//排序 return k<A.k; }}s[M];int main(){ int n; scanf("%d",&n); scanf("%d %d",&s[0].a,&s[0].b); for(int i=1;i<=n;i++){ scanf("%d %d",&s[i].a,&s[i].b); s[i].k=s[i].a*s[i].b; }sort(s+1,s+n+1); Bigint tmp;tmp.val[1]=s[0].a;tmp.num=1; tmp=tmp*s[1].a; ans[1]=tmp/s[1].k;ANS=ans[1]; for(int i=2;i<=n;i++){//这部分就没什么好说的了吧,简直是纯净水 tmp=tmp*s[i].a; ans[i]=tmp/s[i].a/s[i].b; if(ANS<ans[i])ANS=ans[i]; }printf("%d",ANS.val[ANS.num]); for(int i=ANS.num-1;i>=1;i--)printf("%04d",ANS.val[i]); puts(""); return 0;}
C题:
这题的70分是有史以来最简单的70分
但是这题的100分同样是最烦的100分
这题的70分。。。
水,水,水!!!
博主敲完的时候还不敢置信70分竟然这么简单。。
赤裸裸的暴力求最近点以及第二近点,
暴力走点直接70分
代码如下:
PS:这是70分无脑代码#include<bits/stdc++.h>using namespace std;#define M 100005#define oo 2000000000#define LL long longconst LL INF=2e16;void Rd(int &res){ char c;res=0;int k=1; while(c=getchar(),!isdigit(c)&&c!='-'); if(c=='-')k=-1,c=getchar(); do res=(res<<3)+(res<<1)+(c^48); while(c=getchar(),isdigit(c)); res*=k;}int H[M];LL l1[M],l2[M];int nxt2[M],nxt1[M];double res=1e15;int ans;void solve(int f,int pre,int s,int x){ int a=0,len1=0,len2=0; while(1){ if(a==0&&(!nxt2[s]||len1+len2+abs(H[s]-H[nxt2[s]])>x))break;//跳跳跳!!! if(a==1&&(!nxt1[s]||len1+len2+abs(H[s]-H[nxt1[s]])>x))break; if(a==0){ len2+=abs(H[s]-H[nxt2[s]]); s=nxt2[s]; }else { len1+=abs(H[s]-H[nxt1[s]]); s=nxt1[s]; }a=1-a; }if(!f&&len1){//询问1 if(1.0*len2/len1<res){ res=1.0*len2/len1; ans=pre; }else if(1.0*len2/len1==res&&H[pre]>H[ans]){ ans=pre; } }if(f){//询问2 printf("%d %d\n",len2,len1); }}int main(){ int n,m; scanf("%d",&n); for(int i=1;i<=n;i++){ Rd(H[i]); l1[i]=INF;l2[i]=INF; }for(int i=1;i<=n;i++){//暴力寻找nxt1以及nxt2 for(int j=i+1;j<=n;j++){ int h=abs(H[j]-H[i]); if(h<l1[i]){ l2[i]=l1[i]; nxt2[i]=nxt1[i]; l1[i]=h; nxt1[i]=j; }else if(h==l1[i]){ if(H[j]<H[i]){ l2[i]=l1[i]; nxt2[i]=nxt1[i]; l1[i]=h; nxt1[i]=j; }else { l2[i]=h; nxt2[i]=j; } }else if(h<l2[i]){ l2[i]=h; nxt2[i]=j; }else if(h==l2[i]&&H[j]<H[i]){ l2[i]=h; nxt2[i]=j; } } }int x0; scanf("%d",&x0); int High=-oo; for(int i=1;i<=n;i++)//如果是无穷大就有意思了 if(H[i]>High){ High=H[i]; ans=i; } for(int i=1;i<=n;i++)solve(0,i,i,x0); printf("%d\n",ans); scanf("%d",&m); while(m--){ int s,x; Rd(s);Rd(x); solve(1,s,s,x); }return 0;}
然而现在我们要对与这个70分代码进行优化
需要优化的地方有两个,
一个是寻找最近点和次近点(暴力O(n^2))
另一个就是走的过程了(暴力O(n*m))
走的过程需要用倍增这是显然的(O(nlogn))
那么重点就是寻找最近点和次近点了(O(n))
这个还是比较难想的
我们可以使用链表寻找
(NOIP2016初赛试卷中有这个方法)
(用堆博主不知道行不行,可能可以吧)
然后之后就是模拟了,
代码量比较大,调试耗时比较高。。
#include<bits/stdc++.h>using namespace std;#define S 22#define oo 2100000000#define M 100005#define LL long longvoid Rd(int &res){ char c;res=0;int k=1; while(c=getchar(),!isdigit(c)&&c!='-'); if(c=='-')k=-1,c=getchar(); do res=(res<<3)+(res<<1)+(c^48); while(c=getchar(),isdigit(c)); res*=k;}int H[M],rank[M],pre[M],nxt[M],LEN1[M];//H[x]代表城市x的高度//rank[x]就是排序的时候和求nxt以及pre的时候用的。。(映射下标)//pre[x]代表比点x小的最近点的下标//nxt[x]代表比点x大的最近点的下标//LEN1代表到最近点的距离int nxt2[S][M],nxt1[M];//nxt2[i][x]代表在点x跳2^i步到达的点//nxt1代表最近点LL len1[S][M],len2[S][M];//len1[i][x]代表在点x跳2^i步中小B走的距离(即走最近点)//len2[i][x]代表在点x跳2^i步中小A走的距离(即走次近点)double res=1e15;int ans;void Sort(int l,int r){//快排,由于我们需要维护下标的映射比较麻烦,于是干脆手敲sort,直接在sort中交换下标 int x=H[rank[(l+r)/2]],i=l,j=r,temp; while(i<=j){ while(H[rank[i]]<x)i++; while(H[rank[j]]>x)j--; if(i<=j){//这个‘=’必须加,不然会死循环o! temp=rank[i];rank[i]=rank[j];rank[j]=temp; i++;j--; } }if(i<r)Sort(i,r);//博主刚开始的时候逗比了一下把递归调用放到了while里,结果瞬间TLE。。 if(l<j)Sort(l,j);//当时我都吓傻了(倍增也能TLE?一脸懵逼的我调试了半个小时,最后为了保护脑子不炸掉去楼下吹风,一回机房就找出了错误)}void solve(int f,int pre,int s,int x){//倍增跳O(nlogn) int l1=0,l2=0;//没必要long long,题目使其已经<=x了; for(int i=S-1;i>=1;i--) while(nxt2[i][s]&&1ll*l1+l2+len1[i][s]+len2[i][s]<=x){//博主的倍增很烦,开了三个数组 //博主也不知道为什么要用while,正常的倍增应该是用if的,但是博主的daima用if就45分。。。 l1+=len1[i][s];//累加A和B分别走过的距离 l2+=len2[i][s]; s=nxt2[i][s];//跳 } if(nxt2[0][s]&&l1+l2+len2[0][s]<=x){//纯模拟。。。。 l2+=len2[0][s];//累加A走过的距离 s=nxt2[0][s];//跳 if(nxt1[s]&&l1+l2+LEN1[s]<=x){ l1+=LEN1[s];//累加A走过的距离 s=nxt1[s];//跳 } }if(!f&&l1){//询问1 if(1.0*l2/l1<res){//更新答案 res=1.0*l2/l1; ans=pre; }else if(1.0*l2/l1==res&&H[pre]>H[ans]){ ans=pre; } }if(f){//询问2 printf("%d %d\n",l2,l1); }}int main(){ int n,m; scanf("%d",&n); for(int i=1;i<=n;i++){ Rd(H[i]); rank[i]=i; }int hi,sh; Sort(1,n); for(int i=1;i<=n;i++){//用rank数组映射点x的pre以及nxt pre[rank[i]]=rank[i-1]; nxt[rank[i]]=rank[i+1]; }for(int i=1;i<=n;i++){//链表O(n); hi=sh=oo; if(pre[i]) sh=H[i]-H[pre[i]]; if(nxt[i]) hi=H[nxt[i]]-H[i]; if(sh<hi){ nxt1[i]=pre[i];//最近点 LEN1[i]=sh; sh=oo; if(pre[pre[i]]) sh=H[i]-H[pre[pre[i]]]; if(sh<=hi&&sh!=oo){ nxt2[0][i]=pre[pre[i]];//次近点 len2[0][i]=sh; }else if(hi!=oo){//‘!=oo’这个判断尤其重要,缺了必死无疑 nxt2[0][i]=nxt[i];//次近点 len2[0][i]=hi; } }else if(sh>hi){ nxt1[i]=nxt[i];//最近点 LEN1[i]=hi; hi=oo; if(nxt[nxt[i]]) hi=H[nxt[nxt[i]]]-H[i]; if(sh<=hi&&sh!=oo){ nxt2[0][i]=pre[i];//次近点 len2[0][i]=sh; }else if(hi!=oo){ nxt2[0][i]=nxt[nxt[i]];//次近点 len2[0][i]=hi; } }else { if(sh!=oo){ nxt1[i]=pre[i]; LEN1[i]=sh; }if(hi!=oo){ nxt2[0][i]=nxt[i]; len2[0][i]=hi; } }nxt[pre[i]]=nxt[i]; pre[nxt[i]]=pre[i]; }for(int j=1;nxt1[nxt2[0][j]];j++){//倍增的预处理(简单的模拟,可别手抖,很难调试的,博主深有体会) nxt2[1][j]=nxt1[nxt2[0][j]]; len2[1][j]=len2[0][j]; len1[1][j]=LEN1[nxt2[0][j]]; }for(int i=2;i<S;i++){ for(int j=1;nxt2[i-1][nxt2[i-1][j]];j++){ nxt2[i][j]=nxt2[i-1][nxt2[i-1][j]]; len2[i][j]=len2[i-1][j]+len2[i-1][nxt2[i-1][j]]; len1[i][j]=len1[i-1][j]+len1[i-1][nxt2[i-1][j]]; } }int x0; scanf("%d",&x0); int High=-oo; for(int i=1;i<=n;i++)//当询问1所有点出发都为无穷大时 if(H[i]>High){ High=H[i]; ans=i; } for(int i=1;i<=n;i++)solve(0,i,i,x0);//询问1 printf("%d\n",ans); scanf("%d",&m); while(m--){ int s,x; Rd(s);Rd(x); solve(1,s,s,x);//询问2 }return 0;}
于是day1 100+60+70=230算是拿到了基本分吧
不过照这么下去一步一等奖希望很小啊
- NOIP2012复赛提高组day1(A:Vigenère 密码 B:国王游戏 C:开车旅行)
- NOIP2012 提高组 复赛 day1 game 国王游戏 再见
- Noip2012 Day1 T1 Vigenère 密码
- NOIP2012复赛day1 Vigenère密码 简单的一题
- 【jzoj3099】【NOIP2012提高组】【Vigenère密码】
- NOIP 提高组复赛 day1 国王游戏
- NOIP2012 Vigenère密码
- 【noip2012】Vigenère密码
- NOIP2012 Vigenère 密码
- 【NOIP2012】 Vigenère密码
- noip2012 Vigenère密码
- 【noip2012】Vigenère 密码
- NOIP2011复赛提高组day1(A:铺地毯 B:选择客栈 C:mayan游戏)
- NOIP2013复赛提高组day1(A:转圈游戏 B:火柴排队 C:货车运输)
- NOIP 2012 提高组 复赛 day1 drive 开车旅行
- 【NOIP2012提高组】开车旅行
- 【NOIP2012提高组】开车旅行
- 【NOIP2012提高组】开车旅行
- CODEVS 1080线段树练习
- MySQL的表分区详解
- 适配器模式
- JavaWeb开发--Spring开源框架MVC模式的C(分发Servlet)
- 深入理解指针类型间的转换
- NOIP2012复赛提高组day1(A:Vigenère 密码 B:国王游戏 C:开车旅行)
- 欢迎使用CSDN-markdown编辑器
- selenium面向对象之findBy()的使用
- 桥模式
- 不用乘法实现一个数乘以2
- 希尔排序算法
- 还没有为该文档加载任何符号小记
- 《计算机网络》知识总结-3.复用和分用
- 蚂蚁freeline安装教程以及问题解决