zoukankan      html  css  js  c++  java
  • [考试反思]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
    ",tot);
    32     }
    33 }
    View Code

    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
    ",ans<666666666?ans:-1);
    18 }
    View Code

    同理,我们对于某一个特定周期两个灯设它的红灯结束时间是$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)$了。也即$nmsqrt(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
    ",ans<666666666?ans:-1);
    19 }
    View Code

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

    $i ightarrow j : w_j-w_i,j ightarrow k :w_k-w_j$你还会连一个$i ightarrow 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
    ",ans?ans:-1);
    22 }
    View Code

    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]$

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

    如果$jgeq 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 }
    View Code
  • 相关阅读:
    silo 主机 报找不到 grain 实现错误的一个注意
    转:CRT注册
    Maven生命周期
    Maven学习笔记
    Java内存回收机制
    Selenium2.0和1.0的区别
    关于使用Selenium RC无法打开指定页面问题
    四儿子购买手册
    Objective-C 宏定义的收集
    设计模式:适配器模式
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12284149.html
Copyright © 2011-2022 走看看