[NOIP模拟33]反思+题解
又考了一次降智题……
拿到T1秒出正解(可能是因为我高考数学数列学的海星?),分解质因数以后用等比数列求和计算每个因子的贡献。但是当时太过兴奋把最后的
然后去看T3。没什么思路就先打了个暴力,以为最后一个看似不可做的点是给特判分的就打了一堆特判(没想到真的是用来防AK的)。
最后搞T2,实在是搞不懂题就打了个乱搞,样例也可过就扔掉了。
最后对拍T1的时候发现答案完全不对,因为只剩30min辽所以我当场慌的一批,压根就没想改之前的代码,xjb打了一个70分的暴力赶在考试结束前调了出来。
考后4minA掉T1。一个字符的差距。
如果在最后发现自己之前的代码有错误,一定要先想能不能改过来再考虑打暴力止损。一开始心态平稳时想的思路大概率是正确的,如果对拍出错很有可能是细节问题。
A.春思
水题。对A分解质因数,把因子的次数都乘上B就得到了原数。之后考虑因数和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include<cstdio> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const ll mod=9901; const int N=3e6+5; ll a,b; ll fact[N],cnt,mi[N],ans=1; ll qpow(ll x,ll y) { ll res=1;x=x%mod; while (y) { if (y&1)res=res*x%mod; x=x*x%mod; y>>=1; } return res; } int main() { scanf ( "%lld%lld" ,&a,&b); ll tmp=a; for (ll i=2;i*i<=tmp;i++) { if (tmp%i==0) { fact[++cnt]=i;ll num=0; while (tmp&&tmp%i==0)num++,tmp/=i; mi[cnt]=num; } } if (tmp>1)fact[++cnt]=tmp,mi[cnt]=1; for ( int i=1;i<=cnt;i++) { ll m=mi[i]*b; (ans*=(qpow(fact[i],m+1)-1)*qpow(fact[i]-1,mod-2)%mod)%=mod; } cout<<ans%mod<<endl; return 0; } |
B.密州盛宴
显然,如果想符合要求就必须保证每个人随时都在吃,那么自然0越靠前越优,而且0的个数不能超过n个。
考虑比较直观地确定方案是否合法的方式。把0的值赋成-1,从序列末尾往前扫,维护后缀和。一旦某时刻后缀和
所以可以从末尾挑C个0挪到开头,二分C即可。这是70分的做法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #include<cstdio> #include<iostream> #include<cstring> using namespace std; const int N=2e7+5; int n,m,a[N],tot; bool check( int x) { int val=x,sum=0; for ( int i=2*n;i;i--) { if (sum<=-2) return 0; if (a[i]==-1) if (val){val--; continue ;} sum+=a[i]; } if (sum+x*(-1)<=-2) return 0; return 1; } void work() { tot=0; char s[1000005]; for ( int i=1;i<=m;i++) { scanf ( "%s" ,s+1); int len= strlen (s+1),tmp; scanf ( "%d" ,&tmp); while (tmp--) for ( int j=1;j<=len;j++) a[++tot]=(s[j]== '1' ?1:-1); } int cnt0=0; for ( int i=1;i<=2*n;i++) if (a[i]==-1)cnt0++; if (cnt0>n) { puts ( "-1" ); return ; } int l=0,r=2*n,res; while (l<=r) { int mid=l+r>>1; if (check(mid))r=mid-1,res=mid; else l=mid+1; } cout<<res<<endl; } int main() { while ( scanf ( "%d%d" ,&n,&m)==2) { if (!n&&!m) break ; work(); } return 0; } |
可以发现,如果从已经扫到的0里挑一个扔到前面去,目前的后缀和就会+1。所以答案转化为求整个序列后缀和的最小值后取绝对值再-1。
对于每一个给出的循环节,计算这一段的后缀和,并记录过程中后缀和的最小值。对于一整段的后缀和是否>0分类讨论一下更新答案即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<string> #include<vector> using namespace std; typedef long long ll; ll n; int m; vector< int > a[100005]; ll num[100005]; void work() { for ( int i=1;i<=m;i++)a[i].clear(); char s[100005]; ll cnt0=0; for ( int i=1;i<=m;i++) { ll cnt00=0; scanf ( "%s" ,s+1); int len= strlen (s+1); for ( int j=1;j<=len;j++) a[i].push_back(s[j]== '1' ?1:-1),cnt00+=(s[j]== '0' ); scanf ( "%lld" ,&num[i]); cnt00*=num[i]; cnt0+=cnt00; } //cout<<"The num of 0: "<<cnt0<<endl; if (cnt0>n) { puts ( "-1" ); return ; } ll sum=0,cnt=0,ans=0x3f3f3f3f; for ( int k=m;k;k--) { cnt=0;ll minn=0x3f3f3f3f; int sz=a[k].size(); for ( int i=sz-1;i>=0;i--) { cnt+=a[k][i]; minn=min(minn,cnt); } if (cnt>0) ans=min(ans,sum+minn); else ans=min(ans,sum+cnt*(num[k]-1)+minn); sum+=cnt*num[k]; } ans= abs (ans)-1; cout<<(ans>=0?ans:0)<<endl; } int main() { while ( scanf ( "%lld%d" ,&n,&m)==2) { if (!n&&!m) break ; work(); } return 0; } |
C.赤壁情
又是一道神dp……
如果我们能把每一个赤壁之意对应的方案数都求出来,那么就能统计一下再除个阶乘得到答案了。所以把这题转化成计数dp。
我们把形成排列的过程看作把
定义状态数组
考虑插入第i个数对总赤壁之意的贡献:
具体转移还是很繁琐的,有13个转移方程(可以合并成5个)。我们以其中的一个为例:
它的含义是:插入一个数,并且这个数位于一段的左右端点(恰好延长了一段,没有单独成段或连接左右两段),那么它的方案可以是之前基础上从j段里选一个放,并且每一个都可以选左右两端。且对目前的赤壁之意没有影响。
以此类推转移即可。第一维要滚动,因为要枚举段数所以提前算一下范围,还有就是一开始赤壁之意可能为负所以集体加上一个base防止下标溢出。
至于最后一个防AK点……__float128水过好了QAQ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | #include<cstdio> #include<iostream> #include<cstring> #define re register using namespace std; const int base=5005; int n,m,K; int part[105]; namespace qj { __float128 f[2][105][4][10015]; void Main() { int now=1,pre=0; f[now][1][0][-2+base]=1; f[now][1][1][-1+base]=2; f[now][1][2][base]=1; part[1]=1; for (re int i=2;i<=n;i++) { now^=1;pre^=1; part[i]=min(i,n-i+1); int fw=min(5000,i*(i+1)); for (re int j=1;j<=part[i];j++) for (re int l=-fw+base;l<=fw+base;l++) for (re int k=0;k<=2;k++) f[now][j][k][l]=0; for (re int j=1;j<=part[i-1];j++) { for (re int l=-fw+base;l<=fw+base;l++) { #define val0 f[pre][j][0][l] #define val1 f[pre][j][1][l] #define val2 f[pre][j][2][l] //cout<<i<<' '<<val0<<' '<<val1<<' '<<val2<<endl; f[now][j+1][0][l-i*2]+=val0*(j+1); //1 f[now][j][0][l]+=val0*j*2; //2 f[now][j-1][0][l+i*2]+=val0*(j-1); //3 f[now][j+1][1][l-i]+=val0*2; //4 f[now][j][1][l+i]+=val0*2; //5----------------------- f[now][j+1][1][l-2*i]+=val1*j; //6 f[now][j][1][l]+=val1*(j*2-1); //7 f[now][j-1][1][l+2*i]+=val1*(j-1); //8 f[now][j+1][2][l-i]+=val1; //9 f[now][j][2][l+i]+=val1; //10---------------------- f[now][j+1][2][l-2*i]+=val2*(j-1); //11 f[now][j][2][l]+=val2*(j*2-2); //12 f[now][j-1][2][l+2*i]+=val2*(j-1); //13 } } } __float128 ans=0; for (re int i=m;i<=base;i++) ans+=f[now][1][2][i+base]; for (re int i=2;i<=n;i++) ans/=1.0*i; printf ( "%d" ,( int )ans); ans-=( int )ans; putchar ( '.' ); for (re int i=1;i<=K;i++) { ans*=10.0; int t=(ans+(i==K?.5:0)); printf ( "%d" ,t); ans-=t; } printf ( "\n" ); } } double f[2][105][4][10015]; int main() { scanf ( "%d%d%d" ,&n,&m,&K); if (K>=15) { qj::Main(); return 0; } int now=1,pre=0; f[now][1][0][-2+base]=1; f[now][1][1][-1+base]=2; f[now][1][2][base]=1; part[1]=1; for (re int i=2;i<=n;i++) { now^=1;pre^=1; part[i]=min(i,n-i+1); int fw=min(5000,i*(i+1)); for (re int j=1;j<=part[i];j++) for (re int l=-fw+base;l<=fw+base;l++) for (re int k=0;k<=2;k++) f[now][j][k][l]=0; for (re int j=1;j<=part[i-1];j++) { for (re int l=-fw+base;l<=fw+base;l++) { #define val0 f[pre][j][0][l] #define val1 f[pre][j][1][l] #define val2 f[pre][j][2][l] //cout<<i<<' '<<val0<<' '<<val1<<' '<<val2<<endl; f[now][j+1][0][l-i*2]+=val0*(j+1); //1 f[now][j][0][l]+=val0*j*2; //2 f[now][j-1][0][l+i*2]+=val0*(j-1); //3 f[now][j+1][1][l-i]+=val0*2; //4 f[now][j][1][l+i]+=val0*2; //5----------------------- f[now][j+1][1][l-2*i]+=val1*j; //6 f[now][j][1][l]+=val1*(j*2-1); //7 f[now][j-1][1][l+2*i]+=val1*(j-1); //8 f[now][j+1][2][l-i]+=val1; //9 f[now][j][2][l+i]+=val1; //10---------------------- f[now][j+1][2][l-2*i]+=val2*(j-1); //11 f[now][j][2][l]+=val2*(j*2-2); //12 f[now][j-1][2][l+2*i]+=val2*(j-1); //13 } } } double ans=0; for ( int i=m;i<=base;i++) ans+=f[now][1][2][i+base]; for ( double i=2.0;i<=n;i+=1.0) ans/=i; switch (K) { case 0: printf ( "%d\n" ,( int )ans); break ; case 1: printf ( "%.1lf\n" ,ans); break ; case 2: printf ( "%.2lf\n" ,ans); break ; case 3: printf ( "%.3lf\n" ,ans); break ; case 4: printf ( "%.4lf\n" ,ans); break ; case 5: printf ( "%.5lf\n" ,ans); break ; case 6: printf ( "%.6lf\n" ,ans); break ; case 7: printf ( "%.7lf\n" ,ans); break ; case 8: printf ( "%.8lf\n" ,ans); break ; } return 0; } |
本文作者:Rorschach_XR
本文链接:https://www.cnblogs.com/Rorschach-XR/p/11437246.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步