[考试反思]0220省选模拟27:怪异

怪异的一场考试。考的有点懵

$T1$正解是模拟,人人都能切,然而也没有多少人敢笃定这就是正解吧,常数和精度上下卡着,看运气的题了。

$T2$想到了第二档分其实离正解就不远了但是时间不够没往下想。回$T1$卡常去了。

$T3$不给状压分,于是猜到一条结论之后并不敢往下想。。。

然而最后$T1$也没有卡过,考后也在这破玩意上浪费了不少时间。。。

而且$T3$数据特别谁水暴力可以过,然而因为我不会决策点单调,所以学了学知识点,并没有用暴力水过。。。

T1:飞行棋

大意:长度为$n$的序列,$m$人。每个人在一个点丢骰子走路,走到$i$会被传送到$a_i$。最先走到$n$及以后的获胜。求每个人获胜概率(绝对或相对误差$\le 10^{-5}$)。

保证$a_i \neq i$的点最多$20$个。不存在$i \neq a_i$的同时$i+1 \neq a_{i+1}$。$n \le 150,m\le 20$

发现最后两条保证很奇怪。这样的话有一个人获胜并用不了多少轮。

于是大力模拟就好。$dp[i][j]$表示恰好在第$i$轮时初始位置为$j$的人取胜了的概率,转移显然。

卡常就行。如果$m=2$那就需要多模拟几轮。

注意除以$6$进行的次数不要太多,先加起来再除,除法太慢了。

可以过的参数是$100000$轮,如果$m=2$就多跑$50000$轮。

浮点数真的是太慢了,用$long \ long$取代。初值为$10^{17}$。正常转移。最后得到的$dp$值除以$10^{17}$就是概率。

不知道为啥,本机$float$跑$0.35s$,$long \ long$跑$0.9s$。但是后者才能过。。。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 float Dp[155][150003],pre[23][150003],suc[23][150003];int n,m,a[158],s[23];long long dp[155][150003];
 4 int main(){
 5     cin>>n>>m;const int R=m==2?150000:100000;
 6     for(int i=1;i<=n;++i)cin>>a[i];
 7     for(int i=1;i<=m;++i)cin>>s[i];
 8     for(int i=n+1;i<=n+5;++i)a[i]=n;
 9     dp[n][0]=60000000000000000;
10     for(int i=1;i<R;++i)for(int j=1;j<n;++j)if(a[j]==j)
11         dp[j][i]+=(dp[a[j+1]][i-1]+dp[a[j+2]][i-1]+dp[a[j+3]][i-1]+dp[a[j+4]][i-1]+dp[a[j+5]][i-1]+dp[a[j+6]][i-1])/6;
12     for(int i=1;i<=n;++i)for(int j=R-1;~j;--j)dp[i][j]+=dp[i][j+1];
13     for(int i=1;i<=n;++i)for(int j=R-1;~j;--j)dp[i][j]+=60000000000000000-dp[i][1];
14     for(int i=1;i<=n;++i)for(int j=R;j;--j)Dp[i][j]=dp[i][j]/60000000000000000.0;
15     for(int i=1;i<=R;++i)pre[0][i]=suc[m+1][i]=1;
16     for(int i=1;i<=m;++i)for(int j=1;j<=R;++j)pre[i][j]=pre[i-1][j]*Dp[s[i]][j];
17     for(int i=m;~i;--i)for(int j=1;j<=R;++j)suc[i][j]=suc[i+1][j]*Dp[s[i]][j];
18     for(int i=1,x;i<=m;++i){
19         long double ans=0;
20         for(int r=1;r<R;++r)ans+=(Dp[s[i]][r]-Dp[s[i]][r+1])*pre[i-1][r+1]*suc[i+1][r];
21         printf("%.6Lf\n",ans);
22     }
23 }

然而题解有好的多的做法,精度高复杂度低。

就是仍然是一样的dp但是逐轮考虑当前有多少概率所有人都没赢,当这个变量小于$10^{-6}$时跳出。

非常好写,也非常优秀,但是我的确也就懒得写了。于是贴一份yxs大神的代码。

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const double eq=1.0/6;
 7
 8 int n,m;
 9 int a[159],b[159];
10 double c[159],q[159];
11 double f[2][159],p;
12
13 int main(){
14     scanf("%d%d",&n,&m);
15     for(int i=1;i<=n;++i) scanf("%d",&a[i]);
16     for(int i=1;i<=m;++i) scanf("%d",&b[i]),q[i]=1;
17     if(m==1) return puts("1"),0;
18     f[0][n]=p=1; a[n+1]=a[n+2]=a[n+3]=a[n+4]=a[n+5]=a[n];
19     for(int i=1;p>1e-8;++i){
20         const bool cr=i&1;
21         memset(f[cr],0,sizeof(f[cr]));
22         for(int j=1;j<n;++j){
23             for(int k=1;k<=6;++k)
24                 f[cr][j]+=f[!cr][a[j+k]];
25             f[cr][j]/=6;
26         }
27         for(int j=1;j<=m;++j) if(f[cr][b[j]]){
28             p/=q[j]; c[j]+=f[cr][b[j]]*p; q[j]-=f[cr][b[j]]; p*=q[j];
29         }
30     }
31     for(int i=1;i<=m;++i) printf("%lf\n",c[i]);
32     return 0;
33 } 

T2:字符串难题

大意:多个字符串,含有$0,1,?$。问号随机变为$0,1$。求所有情况下,把这些字符串插入$trie$的节点数的和。$n \le 20,|s_i |\le 50$

然而并不难。

转化题意:求所有情况下本质不同的前缀个数之和。

首先考虑都是问号的情况。枚举长度,枚举出现了几种这个长度的前缀,再进行分配,容斥组合数啥的算一算就行。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 998244353
 4 char s[21][55];int n,k,x[2222],y[2222],ans,pc,c[2][2222],rt,len[22],totlen,pw[1111],dp[22][22],C[22][22];
 5 void insert(int&p,char*s,int al=0){
 6     if(!p)p=++pc,ans++;
 7     if(!s[al])return;
 8     insert(c[s[al]-‘0‘][p],s,al+1);
 9 }
10 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
11 int main(){
12     cin>>n;
13     for(int i=1;i<=n;++i)cin>>s[i],len[i]=strlen(s[i]),totlen+=len[i];
14     for(int i=1;i<=n;++i)for(int j=0;s[i][j];++j)if(s[i][j]==‘?‘)k++,x[k]=i,y[k]=j;
15     if(k==totlen){
16         pw[0]=1;
17         for(int i=1;i<=1000;++i)pw[i]=(pw[i-1]<<1)%mod;
18         for(int i=0;C[i][0]=1,i<=20;++i)for(int j=1;j<=i;++j)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
19         for(int i=1;i<=20;++i)for(int j=1;j<=i;++j)dp[i][j]=qp(j,i);
20         for(int i=1;i<=20;++i)for(int j=1;j<=i;++j)for(int k=1;k<j;++k)dp[i][j]=(dp[i][j]-1ll*C[j][k]*dp[i][k]%mod+mod)%mod;
21         for(int l=0;l<=50;++l){
22             int bk=0,pl=1,cnt=0;
23             for(int i=1;i<=n;++i)bk+=len[i]>=l?len[i]-l:len[i],cnt+=len[i]>=l;
24             for(int x=1;x<=cnt&&x<=pw[l];++x)pl=pl*(pw[l]-x+1ll)%mod*qp(x,mod-2)%mod,ans=(ans+1ll*pl*dp[cnt][x]%mod*x%mod*pw[bk])%mod;
25         }return cout<<(ans+mod)%mod<<endl,0;
26     }
27     for(int st=0;st<1<<k;++st){
28         for(int i=0;i<k;++i)s[x[i+1]][y[i+1]]=‘0‘+(st>>i&1);
29         for(int i=1;i<=n;++i)insert(rt,s[i]);
30         for(int i=1;i<=pc;++i)c[0][i]=c[1][i]=0;rt=pc=0;
31     }cout<<ans<<endl;
32 }

然后发现$n$只有$20$。于是枚举子集进行容斥表示子集内前缀相同。容斥系数就是$-1^{|S|-1}$

长度增加$1$时,看可能的前缀数如何变化,全是问号就乘$2$,有$0$有$1$就跳出。

然后就没了吧。。。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 998244353
 4 int n,ans,cnt[22],len[22],pw[1111];char s[22][55];
 5 int main(){
 6     cin>>n;
 7     for(int i=0;i<n;++i)cin>>s[i]+1,len[i]=strlen(s[i]+1);
 8     for(int i=0;i<n;++i)for(int j=1;j<=len[i];++j)cnt[i]+=s[i][j]==‘?‘;
 9     pw[0]=1;for(int i=1;i<=1000;++i)pw[i]=(pw[i-1]+pw[i-1])%mod;
10     for(int st=1;st<1<<n;++st){
11         int pl=0,r=0;
12         for(int i=0;i<n;++i)pl+=cnt[i],r+=st>>i&1;
13         if(st==1)ans=pw[pl];
14         for(int l=1;l<=50;++l){
15             int x1=0,x0=0,q=0;
16             for(int i=0;i<n;++i)if(st&1<<i)
17                 if(!s[i][l])goto xx;
18                 else if(x1&&s[i][l]==‘0‘)goto xx;
19                 else if(x0&&s[i][l]==‘1‘)goto xx;
20                 else if(s[i][l]==‘0‘)x0=1;
21                 else if(s[i][l]==‘1‘)x1=1;
22                 else pl--,q++;
23             if(!(x0|x1))pl++;
24             ans=(ans+(r&1?pw[pl]:mod-pw[pl]))%mod;
25         }xx:;
26     }cout<<ans<<endl;
27 }

T3:障碍雷达

大意:

最大化价值。

首先猜想要么装到最高,要么不装。显然正确,因为费用是一次函数收益是二次的。

然后按照横坐标排序之后分类讨论就挺简单了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 struct P{int x,y;long long c;}p[8888];long long dp[8888],ans;int n,t;string s[4]={".00",".25",".50",".75"};
 4 long long f2(int x){return 1ll*x*x;}
 5 int main(){
 6     cin>>t;while(t--){
 7         cin>>n;ans=0;
 8         for(int i=1;i<=n;++i)scanf("%d%d%lld",&p[i].x,&p[i].y,&p[i].c),p[i].c*=p[i].y<<2;
 9         sort(p+1,p+1+n,[](P a,P b){return a.x<b.x;});p[0].x=-1e9;
10         for(int i=1;i<=n;++i)for(int j=0;j<i;++j)if(p[j].x+p[j].y<=p[i].x+p[i].y)
11             dp[i]=max(dp[i],dp[j]+f2(p[i].y<<1)-f2(max(0,p[j].x+p[j].y-p[i].x+p[i].y))-p[i].c);
12         for(int i=1;i<=n;++i)ans=max(ans,dp[i]),dp[i]=0;
13         cout<<(ans>>2)<<s[ans&3]<<endl;
14     }
15 }

然后想想$n^2dp$可以咋优化。然后睿智的$skyh$说:决策单调性。

于是这题就有决策单调性了。

考虑两种转移,其中要去重的部分它的那个式子看起来就很有决策单调性。

于是就可以按照单调队列维护决策点单调,维护每个点控制的决策区间然后二分就好了。时间复杂度是$O(nlogn)$

然而貌似细节挺多代码也不好写于是写的$skyh$大神的方法。

这种$dp$前后依赖可以用$CDQ$中序遍历那种思想。

然后发现左区间更新右区间时,左边按照右端点排序右边按照左端点排序后,决策点单调。

然后写那种经典的分治就好了。

只不过要注意这里因为一个点要更新多次所以进入分治时初值可能不是0,从而可能因为没有更新最大值而找不到最优决策点。

于是开一个变量记录当前做贡献的区间里的最大值(不一定有原dp值大)就可以了。

时间复杂度是$O(nlog^2n)$的然而常数不大。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 struct P{int x,y;long long c,dp;}p[111111];long long ans;int n,t;string s[4]={".00",".25",".50",".75"};
 4 long long f2(int x){return 1ll*x*x;}
 5 void solve(int l,int r,int L,int R){
 6     if(l>r)return;
 7     int md=l+r>>1,pt=L;long long bst=-1e18;
 8     for(int i=L;i<md&&i<=R;++i)if(p[i].x+p[i].y<=p[md].x+p[md].y)
 9         if(bst<p[i].dp+f2(p[md].y<<1)-f2(max(0,p[i].x+p[i].y-p[md].x+p[md].y))-p[md].c)
10             bst=p[i].dp+f2(p[md].y<<1)-f2(max(0,p[i].x+p[i].y-p[md].x+p[md].y))-p[md].c,pt=i;
11     p[md].dp=max(p[md].dp,bst);
12     solve(l,md-1,L,pt);solve(md+1,r,pt,R);
13 }
14 void cdq(int l,int r){int md=l+r>>1;
15     if(l==r)return;    cdq(l,md);
16     sort(p+l,p+md+1,[](P a,P b){return a.x+a.y<b.x+b.y;});
17     sort(p+md+1,p+r+1,[](P a,P b){return a.x-a.y<b.x-b.y;});
18     solve(md+1,r,l,md);cdq(md+1,r);
19 }
20 int read(){int p=0,f=1;char ch=getchar();
21     while(ch>‘9‘||ch<‘0‘)f=ch==‘-‘?-1:1,ch=getchar();
22     while(ch<=‘9‘&&ch>=‘0‘)p=(p<<3)+(p<<1)+ch-48,ch=getchar();
23     return f*p;
24 }
25 int main(){
26     cin>>t;while(t--){
27         cin>>n;ans=0;
28         for(int i=1;i<=n;++i)p[i].x=read(),p[i].y=read(),p[i].c=read(),p[i].c*=p[i].y<<2;
29         sort(p+1,p+1+n,[](P a,P b){return a.x<b.x;});p[0].x=-1e9;
30         cdq(0,n);
31         for(int i=1;i<=n;++i)ans=max(ans,p[i].dp),p[i].dp=0;
32         cout<<(ans>>2)<<s[ans&3]<<endl;
33     }
34 }

一些常数方面的问题:用$c++11$的函数内部声明比较函数超级快!

然后最后还是要说那个伟大而没脸而有效的大神做法:$O(300n)$。给个复杂度就知道干啥了。

对付$n^2dp$题好像出题人不好好造数据都可以卡枚举界从而拿不少分甚至$AC$

原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12342645.html

时间: 2024-10-12 07:25:42

[考试反思]0220省选模拟27:怪异的相关文章

[考试反思]0113省选模拟6:过载

真累啊...离上次放假也挺久,离下次放假也挺久,离上次放出去玩也挺久,离下次放出去玩还不知道有多久... 好累啊...大脑基本成天在挂机了.什么也不想干了... 持续状态不佳.但是今天运气好,所以考试排名看起来还可以. T1没认真读题也没看数据范围,以为是送分题,17分钟写完交了...然后我就把分送出去了 像个弱智一样...但是现在的精神状态的确不太能支持好好做题 幸亏T2是个比较简单的SAM+dp,思维量不大,脑子宕机的时候也能写一写. (归功于当时给大家讲课时稍微有一点理解,要是其它的板子我

[考试反思]0114省选模拟7:迷离

这次考得相对不错,但是没什么水准. 只不过记得T1这道原题而已.虽说我忘了怎么做,而且数据范围不一样...差不多是从头想的. 但是并没有AC,像个弱智一样,有两个细节写的完全不对还有80分运气也是真好. 其实挂了不止两个细节...以为是原题于是上来就写20分钟写完,然后过一会出一个锅... 然后看T2,感觉$O(nk^2)$也许差不多?常数很大...但也不会别的.挺好想但是不是很好写. 于是乎强烈谴责cbx没素质暴力水题考后还不改正解的无脸行径 于是就开始写,写了一个半小时. 看T3,绝对大神题

[考试反思]0117省选模拟10:争夺

T3出了一点锅,于是按IOI赛制打的. 可能也是这辈子唯一一次好好打的IOI赛制了. 提答,又沉里面了,进去就出不来.莫名的虚荣让我根本没有回头看传统题. 于是的确在T3的80%时间里一直单题rk1,然而其实很慌,剩下两道题又怎样? 运气好,T1特别水,T2数据水,T3用奇技淫巧多拿7分于是并列rk1了. 数组没清空丢了1分... 状态差的不行,一下午一道题都没改出来...咕到第二天也没改出来 于是在第二天考试的时候写完三个暴力之后终于把T2A了... 然后第二天考试就炸了,没什么好说的 T1:

[考试反思]0130省选模拟13:悔恨

0+30+20=50.rk8 关键在于: T1写错个变量名挂了100分 kxkxkxkx(激动的语无伦次) 考试过程?上来看T1,一个比较裸的基环树dp,断开环上一条边之后大概就是一个稍加改动的最大独立集. 思路不难想,细节倒是有一点,考场上调啊调啊过了样例又手模了各种数据都过了两个半小时之后很开心就扔了. 然后看T2/3啥都不会,写了俩随机化就走了. 然而T3随机化+贪心是可以AC的.正确性不知道...没写... 最后还有5分钟回T1,突然发现T1可以不是基环树而是基环树森林??? 啊修锅修锅

[考试反思]0410省选模拟67:迷惑

现在想想,先做$T3$真乃人间迷惑行为. 部分分不多的一场考试,$T1$部分分最多结果没花时间光荣爆零,结果正解真的就是一个暴力+大力分类讨论 $T2$也比较可想,然而看着$75pts$的子任务心中有几分慌张,苟了个暴力跑路了. 结果一个弱智$T3$的$10pts$部分分$O(n^8)$暴写了$2.8k$.从这代码长度看是不是$10pts$您施舍的有点少啊. 其余部分分一点用没有,不知道咋想的. T1:链 大意:维护操作:加边,询问有几个点满足:删掉之后图中剩下的都是若干链.$n,m \le 2

[考试反思]1219省选模拟3: 释怀

有些东西渐渐就远去了...不必挂念. 只有时间才拥有如此力量.我们能做的,唯有释怀. 这次的题格外顺手,大概是我的强项了. 但是考得还是不够好吧...感觉可以更高的 今天迎来了学了OI一年多比较重要的一个成就:(虽说是在考后) AC1000道题!还是挺不容易的. 第1000道题是今天的T3.大部分是自己思考的,题也不是很简单,挺好的. 挺过了联赛,现在想要等到下一次整百,可能就要到3月份了. 我还能在OI多久呢? 回到这场考试. T1的数据范围只有10000,让我想起了ALO那道题50000数据

[考试反思]0110省选模拟5:信仰

又倒一啦 10分真是个熟悉的成绩(虽然没有爆零熟悉) 状态一直低迷,没什么好说的 答题策略的话...就那样吧 一眼看出T1是结论题,没有部分分,不好写. 但T2是伯努利数的差不多是模板题了,于是就奔着它去了. 没有注意$0^0=1$.过不去样例最后交的暴力 没有抽时间认真打T3的暴力,因为至多30分. 因为现在30分和0分对我来说是一样的.我是要靠着省选翻盘,拿大众分的话肯定还是退役,求个稳,4个月白学. 现在的答题策略的确不能像联赛时一样了.现在求稳是赢不了了,在稳住200和争取230里我可能

[考试反思]0122省选模拟12:延迟

博客咕过了一年我也就忘了我考试状态了2333. T1是弱智题但是没想...写个暴力跑路了(时间不够,主要投在T2/3上了) 然而其实想到了一个乱搞,觉得能得分的概率不大,结果数据奇水完全不对的玩意还有20分,然而我并没有写... 然而T2写的是正解,虽说没有其他人的状压优秀,T了一个细节WA了一个拿了80分,凑合吧. 然后T3时间不多的时候写的,拿个暴力,想到了正解大概怎么写但是没有写,太恶心. 考后改题写了写,一下午就过去了...一晚上也就过去了...弄得跟我颓废了半天一样... 然而是真xx

[考试反思]0201省选模拟15:分界

30+10+30+100=170 rk13 17岁的第一仗出师不利. 和外校联考,所以难度不高?后三道题非常简单基本上都是裸的. 但是我做的这是个啥啊... T2广义后缀自动机板子写挂,如果儿子是nq就把儿子修改成nq... T3想到正解然后算错复杂度结果觉得都可以写个简单的于是就把暴力交了上去. T1猜到了第一个结论不会分块暴力也没想到数位dp... 考试前就感觉今天要炸,结果就真炸了. 又当了一次改题大神.半小时一道,中午没睡觉就直接改完了. 下午在uoj上乱跑找好题做ppt.效率挺低的.