九月份
在noip吧看到了CH的NOIP模拟赛广告,于是我毫不犹豫地报名参加了
10.3. 下午
听说晚上的比赛会用NOI 2011的原题,瞬间吓尿。。。
18:30
比赛开始了,第一题一看就是DP,很快搞定
19:00
开始做第二题“兔农”!题目背景用的是NOI 2011的“兔农”原题,被吓尿了,吓昏一段时间后发现好像是个递推水题
19:20
做完第二题好开心,没事干带入一个大数据玩玩,发现十秒钟才出结果,吓昏
昏迷过后发现n最大是一亿,O(n)的递推只能拿到30分,跪了。。。
这个题只能用O(logn)以下复杂度的算法,否则会有70分TLE滚粗
用骗分导论上的方法,我把递推过程中的数字打个表找规律,发现递推过程中会出现循环节,好开心,也许把前十万的递推数据打个表,然后找规律便能在常数时间内找到答案呢!赶紧写一个常数复杂度的程序去~
21:00
写完了第二题的新方法,对拍检查无误,不做啦,第三题打表收场,出去玩咯~
22:00
出去玩回来,比赛结束了,发现我滚粗了,p1 90分,挂在了333这个点(我写程序检查数据不合法性时根本就没考虑到这个点),滚掉10分,p2 50分,因为数组开小了(只打前1W的表),如果开10W的表能拿70分,p3打表完美弄到5分。
最终结果145分,145*2就是290分,在HB弱省就是国一,不过我感觉很忧伤,因为我有30分的失误本不该丢掉。
下面是我瞎诌的解题报告
problem 1
http://ch.ezoj.tk/contest/CH%20Round%20%2354%20-%20Streaming%20%235%20(NOIP%E6%A8%A1%E6%8B%9F%E8%B5%9BDay1)/%E7%8F%A0
序列型动态规划,对2333这样的子序列的终点进行dp即可,注意输入的数字序列中会出现2和3以外的数字,而且可能只有2或只有3,另外子序列可以是顺时针数出来的,也可以是逆时针数出来的,所以一定要小心,稍不留神便会白白丢掉几十分
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #define MAXN 200200 using namespace std; int f[MAXN]; //f[i]=以第i位数字结尾的最大233序列长度 int s[MAXN]; int n; char in[MAXN]; int main() { bool hasTwo=false; scanf("%s",in+1); n=strlen(in+1); for(int i=1;i<=n;i++) { if(in[i]=='2') hasTwo=true; s[i]=in[i]-'0'; s[i+n]=s[i]; } if(!hasTwo) //这里加一个条件判断:输入数字串中没有2,直接输出无解 { printf("TvT\n"); return 0; } for(int i=1;i<=2*n;i++) { if(s[i]==2) f[i]=1; else if(s[i]==3&&(s[i-1]==2||s[i-1]==3)) f[i]=f[i-1]+1; else f[i]=0; } int ans=0,x=1; for(int i=1;i<=2*n;i++) if(f[i]>ans) { ans=f[i]; x=i; } memset(f,0,sizeof(f)); for(int i=2*n;i>=1;i--) { if(s[i]==2) f[i]=1; else if(s[i]==3&&(s[i+1]==2||s[i+1]==3)) f[i]=f[i+1]+1; else f[i]=0; } for(int i=1;i<=2*n;i++) if(f[i]>ans) { ans=f[i]; x=i; } if(ans==0) printf("TvT\n"); else { printf("2"); for(int i=1;i<ans;i++) printf("3"); printf("\n"); } return 0; }
problem 2
http://ch.ezoj.tk/contest/CH%20Round%20%2354%20-%20Streaming%20%235%20(NOIP%E6%A8%A1%E6%8B%9F%E8%B5%9BDay1)/%E5%85%8D%E5%86%9C
虽然说题目描述几乎和NOI 2011的兔农完全相同,但是两个题解法截然不同,此题正解是快速幂,虽然说递推过程中会有减1的情况出现,但是根据网上神犇们的说法,繁殖过程中,死掉一只兔子后,就再也不会死兔子了(之后兔子们永远幸福),或者一只兔子都不会死(一开始兔子们都永远幸福),所以我们只需要判断是否会死兔子,以及在哪一天死兔子,以这一天为分界线分开快速幂即可。
如果打表找循环节的话,开数组很考验RP,虽然是常数复杂度,效果并没有快速幂的方法好
#include <iostream> #include <fstream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include <map> #define MAXN 100000 using namespace std; typedef long long LL; LL n,k,p; /*int fastPow(int x,int pow) { long long int ans=1,base=x; for(;pow;pow>>=1) { if(pow&1) ans=ans*base%p; base=base*base%p; } return ans; }*/ LL fastPow(LL a,LL b) { if(b==0) return 1%p; if(b==1) return a%p; LL tmp=fastPow(a,b>>1); tmp=tmp*tmp%p; if(b&1) tmp=tmp*a%p; return tmp; } LL check() //检查是否会死掉一只兔子,如果会,返回死掉这只兔子的那一天 { LL tmp=1%k; for(LL i=0;i<k;i++) { tmp=tmp+tmp; if(tmp>=k) tmp-=k; if(tmp==1) return i; //返回的i是死掉兔子的那一天 } return -1; //返回-1表明不需要死兔子 } int main() { scanf("%lld%lld%lld",&n,&k,&p); LL day=check(); if(day==-1||n<day) printf("%lld\n",fastPow(2,n+1)); else { LL tmp=(fastPow(2,day+1)+p-1)%p; tmp=tmp*fastPow(2,n-day)%p; printf("%lld\n",tmp); } return 0; }