p1802,p1806,p1808
新高一第二期培训开始了,虽然不是我的班也来快乐了.早上来的时候见到了新高一去军训的场面,还挺感动的.
然后上午并没人问我,我就在校oj刷题,一上午整了三道,美滋滋.
p1802一看就是一个动态规划.考虑每个树能取的状态和前一个树相关,而且不止与前一个树高度相关,还和前前一个树相关.所以我设了四个状态表示当前树的高度和与前一个树的关系.分别是:最低,中等且前一个树更高,中等且前一个树更低,最高.
这样就可以开始转移了,但是如果给f[1]的四个状态都赋值就会使得最后的答案不明确,因为成环的原因嘛.那么可以考虑关于一树的状态再分状态.f[i][j][k]表示1树状态为j,树i状态为k时的最大观赏值.转移也很好转移,答案是f[n][1..4][1..4]中的最大值.但是我懒省事就复制粘贴了四个.
long long a[100010][5],f[100010][5]; long long maxx(long long a,long long b,long long c) { a=a>b?a:b; return a>c?a:c; } int main() { int n=read(); for(int i=1;i<=n;i++) { a[i][1]=read(); a[i][2]=read(); a[i][3]=read(); } //1: f[1][1]=a[1][1]; f[1][2]=f[1][3]=f[1][4]=-10000000; for(int i=2;i<=n;i++) { f[i][1]=a[i][1]+max(f[i-1][4],f[i-1][3]); f[i][2]=a[i][2]+f[i-1][4]; f[i][3]=a[i][2]+f[i-1][1]; f[i][4]=a[i][3]+max(f[i-1][1],f[i-1][2]); } long long ans; ans=max(f[n][3],f[n][4]); //2 f[1][2]=a[1][2]; f[1][1]=f[1][3]=f[1][4]=-10000000; for(int i=2;i<=n;i++) { f[i][1]=a[i][1]+max(f[i-1][4],f[i-1][3]); f[i][2]=a[i][2]+f[i-1][4]; f[i][3]=a[i][2]+f[i-1][1]; f[i][4]=a[i][3]+max(f[i-1][1],f[i-1][2]); } ans=max(ans,f[n][4]); //3 f[1][3]=a[1][2]; f[1][1]=f[1][2]=f[1][4]=-10000000; for(int i=2;i<=n;i++) { f[i][1]=a[i][1]+max(f[i-1][4],f[i-1][3]); f[i][2]=a[i][2]+f[i-1][4]; f[i][3]=a[i][2]+f[i-1][1]; f[i][4]=a[i][3]+max(f[i-1][1],f[i-1][2]); } ans=max(ans,f[n][1]); //4 f[1][4]=a[1][3]; f[1][1]=f[1][2]=f[1][3]=-10000000; for(int i=2;i<=n;i++) { f[i][1]=a[i][1]+max(f[i-1][4],f[i-1][3]); f[i][2]=a[i][2]+f[i-1][4]; f[i][3]=a[i][2]+f[i-1][1]; f[i][4]=a[i][3]+max(f[i-1][1],f[i-1][2]); } ans=maxx(ans,f[n][1],f[n][2]); cout<<ans; return 0; }
p1806读懂了题意后考虑不写程序的话普通人会怎么做.应该先看最后一位能不能++,如果能就++走人,否则看倒数第二位能不能++,并且使得倒数第一位为倒数第二位原来的字母+2.如果能就这样做,以此类推.
那么对于第i位只需判断是否w-i<t-ans[i].如果可以就枚举后面所有位,使得ans[j]=ans[j-1]+1;.
char s,t; int w; char ans[30]; void work() { for(int i=w;i;i--) if(ans[i]<t&&w-i<t-ans[i]) { ans[i]=ans[i]+1; for(int j=i+1;j<=w;j++) ans[j]=ans[j-1]+1; return ; } exit(0); } int main() { s=read()+'a'-1;t=read()+'a'-1;w=read(); for(int i=1;i<=w;i++) cin>>ans[i]; for(int i=1;i<=5;i++) { work(); for(int j=1;j<=w;j++) cout<<ans[j]; cout<<endl; } return 0; }
p1808答案就是c(m+n,m)的后一百位.我会高精度!但是除法不太行,因为求不了逆元.
考虑在班里求组合数,先把分子分母写出来,然后上下消去公因数,然后再把分子乘起来的操作.类比一下,先求出1到m+n的质数并且存起来,对于需要求的组合数,只需要记录m+1到m+n和2到n的数的质因数还剩下多少,再用高精度乘起来.高精度乘单精度还是很好写的.
这道是我上午写的最简单的题了.
long long num[100000],sum[100010],k; long long ans[30]; bool zhi(int x) { for(int i=sqrt(x*1.0);i>=2;i--) if(x%i==0) return 0; return 1; } void Add(long long x) { for(int i=1;x>=num[i]&&i<=k;i++) { while(x%num[i]==0) x=x/num[i],sum[i]++; } return ; } void Minus(long long x) { for(int i=1;x>=num[i]&&i<=k;i++) { while(x%num[i]==0) x=x/num[i],sum[i]--; } return ; } void mul(long long x) { for(int i=1;i<=20;i++) ans[i]=ans[i]*x; for(int i=1;i<=20;i++) ans[i+1]+=ans[i]/100000,ans[i]=ans[i]%100000; return ; } int main() { int m=read(),n=read(); if(m<n) swap(m,n); for(int i=2;i<=m+n;i++) if(zhi(i)) k++,num[k]=i; for(int i=m+n;i>m;i--) Add(i); for(int i=2;i<=n;i++) Minus(i); ans[1]=1; for(int i=1;i<=k;i++) while(sum[i]) mul(num[i]),sum[i]--; for(int i=20;i;i--) { long long t=10000; while(t&&ans[i]/t==0) cout<<0,t=t/10; if(ans[i]) cout<<ans[i]; if(i%2) cout<<endl; } return 0; }
然后中午学弟问了我一道统计数字:
某次科研调查时得到了n(n<=200000)个自然数,每个数均不超过1500000000(1.5*10^9)。已知不相同的数不超过10000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺序输出统计结果。
他们还没学sort,桶排序也不行,这可咋办呢?
我一拍大腿,我会map!
作为map的忠实粉丝,即使map常数很大,但因为它的简单易行(与数组相似),我一直很喜欢用它艹一些题.
map<long long,int>sum;来统计每个数的数量.然后用一个迭代器便利map里的元素输出即可.
map<long long,int> a; long long n,x,maxx; int main() { n=read(); while(n) a[read()]++,n--; map<long long,int>::reverse_iterator iter; for(iter = a.rend(),iter--; iter != a.rbegin(); iter--) { write(iter->first); putchar(32); write(iter->second); putchar(10); } write(iter->first); putchar(32); write(iter->second); return 0; }
最后用map艹翻了一众sort,.