[考试反思]0208省选模拟21:限制

估分35+14+5=54。

一个喜闻乐见的蓝色的零。

撞了$c++11$的关键字$ref$。长见识。

关键是本机因为太慢所以开不开$c++11$,一开就编译好久,所以一直没有发现。。。

通读三道题,没看懂题。再读一遍,啥都不会。然后开始想,然而啥都没有想出来。

上来先看的T2觉得比较简单(?)。然后就开始写。然而少考虑了不少情况,因为数据的特殊性拿到了一些部分分

然后继续想剩下俩题的正解,再然后就没多少时间了(?)。

这时候发现T1是一个非常经典的大小点问题,想的非常麻烦。

最后剩半个多小时的时候匆忙开始写,写完交,CE而不知。

然后看了眼$T3$把能拿的$5$分拿走。

最后觉得不行就去给$T1$写对拍。写了个暴力,这个暴力后来测得能拿到$13$分。

然后开始运行对拍,拍一会错一个,改过来就多对几组。

最后又开始跑对拍,然后我的机子忍受不了对拍了,一阵轰鸣之后我的虚拟机停止了工作。。。

于是乎,暴力和打的所谓正解都没有交上去,然后依然是那个蓝色的零。。。

然而最后$T1$我写的那个的确能拿$35$分。。。没$AC$的原因是数组开销=小了

改题?我也不知道为啥这么顺,这场考试的题看了题解好像就很简单了。

T2并没有写最终的正解,转而写了和考场上思路一样的那个,复杂度也是对的应该不算在水题吧。。。

最终的正解也看懂了但是好像挺恶心的于是懒得写了。。。

我错了我错了现在我两种都写了。。。。

T1:灯

大意:序列每个点有一种颜色,每次操作会使某一种颜色的所有灯改变状态,初始都没开。每次操作后询问序列有几个极长连续亮灯段。$n,m,q \le 10^5$

联通块数=边数-点数。点数直接开数组维护,边数在每次修改时统计。

抽象成图上的问题之后,我们就又可以想起大小点做法了。根据颜色出现次数的多少划分大小点。

维护一个变量表示对于某种大颜色“两侧的所有亮着的小点的数量”。

修改一个小点时,暴力枚举其出现位置的两侧统计贡献,并且更新上述变量。

修改一个大点时,暴力枚举所有大点讨论相互影响更新答案,再根据上述变量更新小点对该大点的影响。

所以只需要再预处理大点之间的影响即可。时间复杂度$O(q \sqrt(n))$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 111111
 4 int a[S],cnt[S],al[S],n,m,q,big[345],bc,tot,ed[S],crs[345][345],re[S];
 5 vector<int>v[S];
 6 int main(){
 7     //freopen("2.in","r",stdin);freopen("1.out","w",stdout);
 8     scanf("%d%d%d",&n,&m,&q);
 9     for(int i=1;i<=n;++i){scanf("%d",&a[i]);if(a[i]==a[i-1])i--,n--;}a[n+1]=0;
10     for(int i=1;i<=n;++i)cnt[a[i]]++,v[a[i]].push_back(i);
11     for(int i=1;i<=m;++i)if(cnt[i]>=300)big[++bc]=i,re[i]=bc;
12     for(int i=2;i<=n;++i)crs[re[a[i-1]]][re[a[i]]]++,crs[re[a[i]]][re[a[i-1]]]++;
13     while(q-->0){
14         int x;scanf("%d",&x);
15         if(al[x]){
16             tot-=cnt[x];
17             if(re[x]){
18                 tot+=ed[x];
19                 for(int i=1;i<=bc;++i)if(al[big[i]])tot+=crs[i][re[x]];
20             }else for(int i=0;i<v[x].size();++i)
21                 tot+=al[a[v[x][i]-1]],ed[a[v[x][i]-1]]--,tot+=al[a[v[x][i]+1]],ed[a[v[x][i]+1]]--;
22             al[x]=0;
23         }else{
24             tot+=cnt[x];al[x]=1;
25             if(re[x]){
26                 tot-=ed[x];
27                 for(int i=1;i<=bc;++i)if(al[big[i]])tot-=crs[i][re[x]];
28             }else for(int i=0;i<v[x].size();++i)
29                 tot-=al[a[v[x][i]-1]],ed[a[v[x][i]-1]]++,tot-=al[a[v[x][i]+1]],ed[a[v[x][i]+1]]++;
30         }
31         printf("%d\n",tot);
32     }
33 }

T2:十字路口

大意:n个红绿灯,红灯时有倒计时。你在m个时刻观察了所有灯。已知所有灯有一个公共周期,周期内某一个固定时刻红转绿,某个固定时刻绿转红。求周期长。$nm \le 100000$

对于同一盏灯的两个观测时刻$t_1,t_2$,如果我们观察到它的红灯倒计时为$x,y$。设周期为$T$

我们知道$t_1+x \equiv t_2+y (mod \ T)$。因为这两个时刻都是绿灯亮起的时刻在每个周期里只有一次,所以对$T$同余。

据此列式,把关系建成边,点是观察的时刻,边权是红灯倒计时的差值。这样建出的图中,每个环长都是$T$的倍数。

暴力建图,$floyd$找环,取最小环。时间复杂度$O(nm^2+m^3)$。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,ans=666666666,e[333][333];vector<vector<int> >v;
 4 int main(){
 5     cin>>m>>n;if(n>300&&m>300)return 0;
 6     v.resize(n+1);
 7     for(int i=1;i<=n;++i)v[i].resize(m+1);
 8     for(int j=1;j<=n;++j)for(int i=1;i<=m;++i)scanf("%d",&v[j][i]);
 9     memset(e,0x3f,sizeof e);
10     if(n>m)for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(v[i][j])for(int k=1;k<=m;++k)if(v[i][k]>v[i][j])
11         e[j][k]=min(e[j][k],v[i][k]-v[i][j]);
12     if(n<=m)for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)if(v[j][i])for(int k=1;k<=n;++k)if(v[k][i]>v[j][i])
13         e[j][k]=min(e[j][k],v[k][i]-v[j][i]);
14     for(int i=1;i<=m;++i)e[i][i]=0;
15     for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)for(int k=1;k<=m;++k)e[j][k]=min(e[j][k],e[j][i]+e[i][k]);
16     for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)if(i!=j)ans=min(ans,e[i][j]+e[j][i]);
17     printf("%d\n",ans<666666666?ans:-1);
18 }

同理,我们对于某一个特定周期两个灯设它的红灯结束时间是$t_1,t_2$,某一时刻两个灯的红灯倒计时为$x,y$。设周期为$T$

我们知道$t1-x \equiv t2-y (mod \ T)$。因为做差之后就是当前的时刻。

和上面的那种方法同理,时间复杂度是$O(mn^2+n^3)$

如果我们根据$n,m$的大小关系决定运行上面哪个算法,这样复杂度就是$nm \ min(n,m)$了。也即$nm\sqrt(nm)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,ans=666666666,e[333][333];vector<vector<int> >v;
 4 int main(){
 5     cin>>m>>n;
 6     v.resize(n+1);
 7     for(int i=1;i<=n;++i)v[i].resize(m+1);
 8     for(int j=1;j<=n;++j)for(int i=1;i<=m;++i)scanf("%d",&v[j][i]);
 9     memset(e,0x3f,sizeof e);
10     if(n>m)for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(v[i][j])for(int k=1;k<=m;++k)if(v[i][k]>v[i][j])
11         e[j][k]=min(e[j][k],v[i][k]-v[i][j]);
12     if(n<=m)for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)if(v[j][i])for(int k=1;k<=n;++k)if(v[k][i]>v[j][i])
13         e[j][k]=min(e[j][k],v[k][i]-v[j][i]);
14     if(n<=m)swap(n,m);
15     for(int i=1;i<=m;++i)e[i][i]=0;
16     for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)for(int k=1;k<=m;++k)e[j][k]=min(e[j][k],e[j][i]+e[i][k]);
17     for(int i=1;i<=m;++i)for(int j=1;j<=m;++j)if(i!=j)ans=min(ans,e[i][j]+e[j][i]);
18     printf("%d\n",ans<666666666?ans:-1);
19 }

啊貌似忘说正解了。你发现你连的边都是做差的,而这种关系具有传递性。

$i \rightarrow j : w_j-w_i,j\rightarrow k :w_k-w_j$你还会连一个$i \rightarrow k : w_k-w_i$

然而第三条边显然是没有用的。。。于是我们直接按照权值排序之后相邻的点建边就能取代原图

然后$dfs$找到的简单环就是周期了。代码没有写,因为和上个做法一样所以没啥必要。

在大佬们的压迫之下最后又写了一份正解。。。代码长了30B。。。但是只快了300ms。理论复杂度$O(nmlogn)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 666666
 4 int n,m,fir[S],ec,l[S],d[S],v[S],to[S],ins[S],al[S],ans,a[S],op;vector<int>V[S];
 5 void link(int a,int b,int w){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=w;}
 6 bool cmp(int a,int b){return V[op][a]<V[op][b];}
 7 void dfs(int p,int D){
 8     d[p]=D;al[p]=1;ins[p]=1;
 9     for(int i=fir[p];i;i=l[i])if(!al[to[i]])dfs(to[i],D+v[i]);else if(ins[to[i]])ans=v[i]+D-d[to[i]];
10     ins[p]=0;
11 }
12 int main(){
13     cin>>m>>n;
14     for(int i=1;i<=n;++i)V[i].resize(m+1);
15     for(int j=1;j<=n;++j)for(int i=1;i<=m;++i)scanf("%d",&V[j][i]);
16     for(int i=1;i<=m;++i)a[i]=i;
17     for(op=1;op<=n;++op){
18         sort(a+1,a+1+m,cmp);unique(a+1,a+1+m,cmp);
19         for(int i=2;i<=m;++i)if(V[op][a[i-1]]==V[op][a[i]])swap(a[i-1],a[i]);else if(V[op][a[i-1]])link(a[i-1],a[i],V[op][a[i]]-V[op][a[i-1]]);
20     }for(int i=1;i<=m;++i)if(!al[i])dfs(i,0);
21     printf("%d\n",ans?ans:-1);
22 }

T3:密室逃脱

大意:i号点与i+1号点连边,i与i+1能相互到达当且仅当i号点上有$a_i$个人不在移动或$i+1$号点上有$b_i$人没在移动。求序列上最多能放多少人让他们到不了1号点。

$n \le 1000$,边权$\le 10000$

神仙dp神仙考场切啊。。。

当人数足够多时我们发现这一大批人可以一起想到哪里就到哪里。我们称他们能到达的区间为一段。

具体而言定义一段是段内所有人可以一起到达段内任意位置,这称之为一段。

设$dp[i][j]$表示$i$号节点所在的段中有$j$个人,此时前$i$个点能放多少人。

玩家们会采取最优决策。所以$dp$转移比较显然。我们从$dp[i][j]$向外扩展。

若$j <a[i]$那么$i$上的人并不能靠自己向右走,所以考虑在$i+1$点上放人,如果放的人不到$b[i]$个那$i$号点的人的确就走不到i+1$了。

也即$dp[i+1][0,1,...,b[i]-1]=dp[i][j]+(0,1,...,b[i]-1)$

而如果你放的人达到了$b[i]$个左边的人就往右边涌了。$dp[i+1][j+b[i]]=dp[i][j]+b[i]$

如果左侧的人数达到了$a[i]$那么它们当中的$a[i]$个人负责按按钮剩下的人就可以往右走了。

$dp[i+1][j-a[i]]=dp[i][j]$

不考虑在右面在放人了,反正都能走过来在左边放也是一样的。

如果$j\geq a[i]+b[i]$那两边就连成一段可以自由来回了。

$dp[i+1][j]=dp[i][j]$。也不考虑多放人因为左边可以走过去。

数组的第二维开多大?这里需要理解深刻一点了不然就会向我一样$WA$。

$20000$。因为对于一个段如果里面的人数超过了2倍最大边权它就一定走得到0好点。而不到2倍就有可能走不到。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int dp[1111][21111],a[1111],b[1111],n,m,ans;
 4 int main(){
 5     scanf("%d%d",&n,&m);memset(dp,0x80,sizeof dp);
 6     for(int i=1;i<m;++i)dp[1][i]=i;dp[1][0]=0;
 7     for(int i=1;i<n;++i)scanf("%d%d",&a[i],&b[i]);
 8     for(int i=1;i<n;++i){
 9         int mx=0x80000000;
10         for(int j=0;j<a[i];++j)dp[i+1][j+b[i]]=max(dp[i+1][j+b[i]],dp[i][j]+b[i]),mx=max(mx,dp[i][j]);
11         for(int j=0;j<b[i];++j)dp[i+1][j]=max(dp[i+1][j],mx+j);
12         for(int j=a[i];j<a[i]+b[i];++j)dp[i+1][j-a[i]]=max(dp[i+1][j-a[i]],dp[i][j]);
13         for(int j=a[i]+b[i];j<=20000;++j)dp[i+1][j]=max(dp[i+1][j],dp[i][j]);
14     }for(int i=0;i<=20000;++i)ans=max(ans,dp[n][i]);cout<<ans<<endl;
15 }

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

时间: 2024-11-06 07:15:33

[考试反思]0208省选模拟21:限制的相关文章

[考试反思]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可以不是基环树而是基环树森林??? 啊修锅修锅

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

怪异的一场考试.考的有点懵 $T1$正解是模拟,人人都能切,然而也没有多少人敢笃定这就是正解吧,常数和精度上下卡着,看运气的题了. $T2$想到了第二档分其实离正解就不远了但是时间不够没往下想.回$T1$卡常去了. $T3$不给状压分,于是猜到一条结论之后并不敢往下想... 然而最后$T1$也没有卡过,考后也在这破玩意上浪费了不少时间... 而且$T3$数据特别谁水暴力可以过,然而因为我不会决策点单调,所以学了学知识点,并没有用暴力水过... T1:飞行棋 大意:长度为$n$的序列,$m$人.每

[考试反思]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