NOIp 2012 Day1 解题报告
1. Vigenère密码
一道模拟题。发现规律就很好做。
先把所有的字母都变成小写,然后再做。输出时要把原来是大写的字母再变成大写。
复杂度O(N)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable// 9 char key[1000],mword[1000],rword[1000]; 10 bool up[1000]={0}; 11 12 //solve// 13 int main(){ 14 gets(key); 15 gets(mword); 16 int l1=strlen(key),l2=strlen(mword); 17 for (int i=0;i<l1;++i){ 18 if (key[i]<'a'){ 19 key[i]+='a'-'A'; 20 } 21 } 22 for (int i=0;i<l2;++i){ 23 if (mword[i]<'a'){ 24 mword[i]+='a'-'A'; 25 up[i]=1; 26 } 27 } 28 for (int i=0,j=0;i<l2;++i,j=(j+1)%l1){ 29 char word=mword[i]-'a'; 30 word-=key[j]-'a'; 31 word+=52; 32 word%=26; 33 rword[i]=word+'a'; 34 } 35 for (int i=0;i<l2;++i){ 36 if (up[i]){ 37 rword[i]-='a'-'A'; 38 } 39 } 40 puts(rword); 41 return 0; 42 }
2. 国王游戏
一开始没有思路。但是分析一下数据范围,高精度的复杂度并不小,所以在用高精度的时候必须是O(n)算法才能过所有数据。那么就意味着顺序是在求答案之前确定的,那么它一定是按照每个人左右手两个数的某种关系排序得到的顺序。
我们发现,一个人得到的奖赏是他及他前面所有的人的左手数字之积除以他自己左右手数字的乘积。依据贪心思想,由于左手的乘积在任何顺序下都随着标号的递增而递增,那么左右手乘积较大的人应该排在较后的位置。
于是,按照每个人左右手乘积从小到大排序。用高精度循环每个人,求最大值。
复杂度O(NlogN+NK)
本人目前还没有得出该贪心正确性的严谨证明。不过一种不错的方法是:写出一个全排列暴力,和它对拍来验证贪心是否正确(我就是这么干的)。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //constant// 9 const int mod=10; 10 11 //type// 12 struct hand{ 13 int a,b; 14 }; 15 struct rec{ 16 int len,x[5000]; 17 }; 18 19 //variable// 20 int n,hl,hr; 21 hand num[1010]; 22 rec pai={1,{0}},ans={1,{0}},kkk; 23 24 //function prototype// 25 bool comp(hand,hand); 26 bool operator <(rec,rec); 27 rec operator *(rec,int); 28 rec operator /(rec,int); 29 void print(rec); 30 31 //solve// 32 int main(){ 33 scanf("%d%d%d",&n,&hl,&hr); 34 for (int i=1;i<=n;++i){ 35 scanf("%d%d",&num[i].a,&num[i].b); 36 } 37 sort(num+1,num+n+1,comp); 38 pai.len=0; 39 while (hl){ 40 pai.x[++pai.len]=hl%10; 41 hl/=10; 42 } 43 if (!pai.len) ++pai.len; 44 for (int i=1;i<=n;++i){ 45 kkk=pai/num[i].b; 46 if (ans<kkk){ 47 ans=kkk; 48 } 49 pai=pai*num[i].a; 50 } 51 print(ans); 52 return 0; 53 } 54 55 void print(rec x){ 56 for (int i=x.len;i;--i){ 57 printf("%d",x.x[i]); 58 }puts(""); 59 } 60 61 bool comp(hand a,hand b){ 62 return a.a*a.b<b.a*b.b; 63 } 64 65 bool operator <(rec a,rec b){ 66 if (a.len<b.len){ 67 return true; 68 }else if (a.len>b.len){ 69 return false; 70 }else{ 71 for (int i=a.len;i;--i){ 72 if (a.x[i]<b.x[i]){ 73 return true; 74 }else if (a.x[i]>b.x[i]){ 75 return false; 76 } 77 } 78 return false; 79 } 80 } 81 rec operator *(rec a,int b){ 82 for (int i=1;i<=a.len;++i){ 83 a.x[i]*=b; 84 } 85 for (int i=1;i<=a.len+10;++i){ 86 a.x[i+1]+=a.x[i]/mod; 87 a.x[i]%=mod; 88 } 89 a.len+=5; 90 while (!a.x[a.len]) --a.len; 91 if (!a.len) ++a.len; 92 return a; 93 } 94 95 rec operator /(rec a,int b){ 96 rec c={1,{0}}; 97 for (int i=a.len;i;--i){ 98 a.x[i]+=a.x[i+1]*mod; 99 c.x[i]+=a.x[i]/b; 100 a.x[i]%=b; 101 } 102 c.len=a.len+5; 103 while (!c.x[c.len]) --c.len; 104 if (!c.len) ++c.len; 105 return c; 106 }
3. 开车旅行
分析:
由于N较大,且距离满足前缀和性质,于是想到倍增。
首先预处理出在每个点,A和B会走向的点和走的距离。C++可以用set,Pascal可以直接用二叉查找树(我没有亲身测试,但应该不会被卡)。
设dis[i][j][0..1],dest[i][j]分别代表第一次A开车,从i开始向前走2的j次方步(任何一个人开了一次车就是一步),A的路程和B的路程以及会到哪个点。这里要注意的是,j为1的时候,倍增转移的是B,注意特判。
对于第一个问题,枚举起点,倍增算路径即可。
对于第二个问题,也是倍增算路径。
优化:
我们发现上面的dis和dest,在j不为1的时候倍增转移的都是关于A的值,于是将上文的“步”转换为“轮”,即A和B各开车一次。但是这样在求答案的时候,从17到0枚举完倍增量j,还要判断A是否能再向前走一步。
用set预处理的过程中,为了不用啰嗦的判定是否超界,可以在里面加入几个极大和极小值。但是一定要保证它们够大或者够小。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<set> 6 #include<algorithm> 7 using namespace std; 8 9 //type// 10 struct rec{ 11 int x; 12 long long h; 13 }; 14 15 //variable// 16 int n,m,dest[100100][18]; 17 set<rec> h; 18 long long dis[100100][18][2],height[100100],d[100100][2],pos[100100][2]; 19 20 //funcytion prototype// 21 void calc_delta(void); 22 void calc_dis(void); 23 bool operator <(rec,rec); 24 void calc_ans1(void); 25 void calc_ans2(void); 26 27 //solve// 28 int main(){ 29 scanf("%d",&n); 30 h.insert((rec){-1,-10000010000ll});h.insert((rec){-1,-10000001111ll}); 31 h.insert((rec){-1,20000001000ll});h.insert((rec){-1,20000001111ll}); 32 for (int i=1;i<=n;++i){ 33 scanf("%lld",height+i); 34 h.insert((rec){i,height[i]}); 35 } 36 calc_delta(); 37 calc_dis(); 38 calc_ans1(); 39 calc_ans2(); 40 return 0; 41 } 42 43 void calc_ans2(){ 44 int m; 45 scanf("%d",&m); 46 while (m--){ 47 int si; 48 long long xi; 49 scanf("%d%lld",&si,&xi); 50 int loc=si; 51 long long la=0,lb=0,len=0; 52 for (int j=17;j>=0;--j){ 53 if (dest[loc][j]&&dis[loc][j][0]+dis[loc][j][1]+len<=(long long)xi){ 54 len+=dis[loc][j][0]+dis[loc][j][1]; 55 la+=dis[loc][j][0]; 56 lb+=dis[loc][j][1]; 57 loc=dest[loc][j]; 58 } 59 } 60 if (pos[loc][0]&&len+(long long)d[loc][0]<=(long long)xi){ 61 la+=d[loc][0]; 62 } 63 printf("%lld %lld\n",la,lb); 64 } 65 } 66 67 void calc_ans1(){ 68 long long x0; 69 cin>>x0; 70 int ansloc=0,anshei=-1e9-1e5; 71 long long ansfz=1e9,ansfm=0; 72 for (int i=1;i<=n;++i){ 73 int loc=i; 74 long long la=0,lb=0,len=0; 75 for (int j=17;j>=0;--j){ 76 if (dest[loc][j]&&dis[loc][j][0]+dis[loc][j][1]+len<=(long long)x0){ 77 len+=dis[loc][j][0]+dis[loc][j][1]; 78 la+=dis[loc][j][0]; 79 lb+=dis[loc][j][1]; 80 loc=dest[loc][j]; 81 } 82 } 83 if (pos[loc][0]&&len+(long long)d[loc][0]<=(long long)x0){ 84 la+=d[loc][0]; 85 } 86 if ((lb&&la*ansfm<ansfz*lb)||(lb&&la*ansfm==ansfz*lb&&height[i]>anshei)){ 87 ansloc=i; 88 anshei=height[i]; 89 ansfz=la,ansfm=lb; 90 } 91 } 92 printf("%d\n",ansloc); 93 } 94 95 bool operator <(rec a,rec b){ 96 return a.h<b.h; 97 } 98 99 void calc_delta(){ 100 for (int i=1;i<=n;++i){ 101 set<rec>::iterator ite=h.find((rec){i,height[i]}); 102 set<rec>::iterator prev=ite,succ=ite; 103 --prev,++succ; 104 if ((long long)height[i]-(*prev).h<=(*succ).h-(long long)height[i]){ 105 pos[i][1]=(*prev).x; 106 d[i][1]=height[i]-(*prev).h; 107 --prev; 108 }else{ 109 pos[i][1]=(*succ).x; 110 d[i][1]=(*succ).h-(long long)height[i]; 111 ++succ; 112 } 113 if ((long long)height[i]-(*prev).h<=(*succ).h-(long long)height[i]){ 114 pos[i][0]=(*prev).x; 115 d[i][0]=(long long)height[i]-(*prev).h; 116 }else{ 117 pos[i][0]=(*succ).x; 118 d[i][0]=(*succ).h-(long long)height[i]; 119 } 120 h.erase(ite); 121 } 122 for (int i=1;i<=n;++i){ 123 for (int p=0;p<2;++p){ 124 if (pos[i][p]<=0){ 125 pos[i][p]=d[i][p]=0; 126 } 127 } 128 } 129 } 130 131 void calc_dis(){ 132 for (int i=1;i<=n;++i){ 133 dis[i][0][0]=d[i][0]; 134 dis[i][0][1]=d[pos[i][0]][1]; 135 dest[i][0]=pos[pos[i][0]][1]; 136 } 137 for (int j=1;j<18;++j){ 138 for (int i=1;i<=n;++i){ 139 for (int p=0;p<2;++p){ 140 dest[i][j]=dest[dest[i][j-1]][j-1]; 141 dis[i][j][p]=dis[i][j-1][p]+dis[dest[i][j-1]][j-1][p]; 142 dis[i][j][p^1]=dis[i][j-1][p^1]+dis[dest[i][j-1]][j-1][p^1]; 143 } 144 } 145 } 146 }