[考试反思]0115省选模拟8:等待

不好。但凑和。

再也不相信对拍了(然而肯定还是要写的)

T1又是一个根号算法(当然可以更优),没想到,写了一个剪枝暴力,本来从复杂度上来说应该有80分,但是因为没有发现

「加0不算对一个数进行修改」

一个细节没注意,挂了45分。

还写对拍了,但是暴力也没有注意这个细节,于是二者愉快的拍上了。。。

T3的话写了个迷之网络流,感觉不太对于是愉快的写了一个状压。

话说这辈子第一次靠自己不抄代码把$tarjan$写对,开心。(我为啥没联赛退役?因为它没考啊)

又愉快的拍上了二十万组。于是自信的交了网络流。

结果网络流20分,状压50分。。。

就这些弱智玩意送了小一百分。。。能混到这个rank就靠一个T2

然而T2是我三道题里花时间最少的,也没写对拍,反而得分最多。。。

运气好,爱用STL,在别人开数组的地方开了一个$unoredred map$。数据微锅的情况下没有$RE$。靠非实力因素多拿了$13$分。

所以其实rank应该再掉两个。。。

考得还是挺烂的,但是这次除了没认真注意细节以外貌似是尽力了。。。

所以我也做不出什么评价。只能继续加油吧。。。

T1:序列

题意:维护序列支持区间加,区间取$max$,询问单点值及从开始到现在为止的变化次数。$n,mle 100000$

内存限制$32MB$。时间限制$1000ms$

线段树,假装小清新,维护最大值与最小值并注意+0不算改变后可以拿到80分。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 400002
 4 #define ll long long
 5 #define lc p<<1
 6 #define rc p<<1|1
 7 #define md (cl+cr>>1)
 8 ll lz[S],mn[S],mx[S];int n,a[S],m,tms[S];char s[9];
 9 void up(int p){mn[p]=min(mn[lc],mn[rc]);mx[p]=max(mx[lc],mx[rc]);}
10 void build(int p=1,int cl=1,int cr=n){
11     if(cl==cr){mn[p]=mx[p]=a[cl];return;}
12     build(lc,cl,md);build(rc,md+1,cr);up(p);
13 }
14 void down(int p){
15     if(tms[p])tms[lc]+=tms[p],tms[rc]+=tms[p],tms[p]=0;
16     if(mn[p]==mx[p])mn[lc]=mn[rc]=mx[lc]=mx[rc]=mn[p],lz[p]=0;
17     else if(lz[p])mn[lc]+=lz[p],mn[rc]+=lz[p],mx[lc]+=lz[p],mx[rc]+=lz[p],lz[lc]+=lz[p],lz[rc]+=lz[p],lz[p]=0;
18 }
19 void Max(int l,int r,int w,int p=1,int cl=1,int cr=n){
20     if(w<=mn[p])return;
21     if(l<=cl&&cr<=r&&w>mx[p]){tms[p]++;lz[p]=0;mn[p]=mx[p]=w;return;}
22     down(p);
23     if(l<=md)Max(l,r,w,lc,cl,md);
24     if(r>md)Max(l,r,w,rc,md+1,cr);
25     up(p);
26 }
27 void add(int l,int r,int v,int p=1,int cl=1,int cr=n){
28     if(v==0)return;
29     if(l<=cl&&cr<=r){
30         if(mn[p]==mx[p])mn[p]+=v,mx[p]+=v,lz[p]=0;
31         else lz[p]+=v,mn[p]+=v,mx[p]+=v;
32         tms[p]++;return;
33     }
34     down(p);
35     if(l<=md)add(l,r,v,lc,cl,md);
36     if(r>md)add(l,r,v,rc,md+1,cr);
37     up(p);
38 }
39 int ask(int x,int p=1,int cl=1,int cr=n){
40     if(cl==cr)return p;
41     down(p);
42     return x<=md?ask(x,lc,cl,md):ask(x,rc,md+1,cr);
43 }
44 int main(){//freopen("1.in","r",stdin);freopen("1.out","w",stdout);
45     scanf("%d",&n);
46     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
47     build();
48     scanf("%d",&m);while(m-->0){
49         scanf("%s",s);int a,b,c;
50         if(s[0]=='A')scanf("%d%d%d",&a,&b,&c),add(a,b,c);
51         if(s[0]=='M')scanf("%d%d%d",&a,&b,&c),Max(a,b,c);
52         if(s[0]=='Q')scanf("%d",&a),a=ask(a),printf("%lld %d
",mn[a],tms[a]);
53     }
54 }
View Code

正解很多,能看懂的就一个分块。块内部排序,维护加法标记和「已经进行过的取max中的最大值」两个标记。

有序之后,变化次数的累加可以差分,而且如果整个块都被操作的话单调性不变。

然后T的还不如线段树。把块长开成36之后它AC了???36是个什么块啊?????

理论复杂度$O(nsqrt{n} log n)$。然而我常数写太丑了。。。卡过去的

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 400002
 4 #define ll long long
 5 ll lz[S],mx[S];int bc,n,m,tms[S],cl[S],cr[S],bl[S],B,lzt[S],pt[S];char s[9];
 6 struct data{long long v;int p;}da[S];
 7 bool cv(data x,data y){return x.v<y.v;}
 8 bool cp(data x,data y){return x.p<y.p;}
 9 void down(int x){
10     for(int i=cr[x]-1;i>=cl[x];--i)lzt[i]+=lzt[i+1];
11     for(int i=cl[x];i<=cr[x];++i)tms[da[i].p]+=lzt[i],lzt[i]=0;
12     for(int i=cl[x];i<=cr[x];++i)da[i].v+=lz[x];
13     for(int i=cl[x];i<=cr[x];++i)if(da[i].v<mx[x])da[i].v=mx[x];
14     lz[x]=0;mx[x]=-123456789012345;pt[x]=cl[x];
15     sort(da+cl[x],da+cr[x]+1,cp);
16 }
17 void up(int x){sort(da+cl[x],da+cr[x]+1,cv);}
18 int main(){//freopen("1.in","r",stdin);freopen("1.out","w",stdout);
19     scanf("%d",&n);
20     for(int i=1;i<=n;++i)scanf("%lld",&da[i].v),da[i].p=i;
21     B=36;bc=n/B;
22     for(int i=1;i<=bc;++i)cl[i]=i*B-B+1,cr[i]=i*B;
23     if(cr[bc]!=n)cr[++bc]=n,cl[bc]=cr[bc-1]+1;
24     for(int i=1;i<=bc;++i)for(int j=cl[i];j<=cr[i];++j)bl[j]=i;
25     for(int i=1;i<=bc;++i)up(i),mx[i]=-123456789012345,pt[i]=cl[i];
26     scanf("%d",&m);while(m-->0){
27         scanf("%s",s);int a,b,c;
28         if(s[0]=='A'){
29             scanf("%d%d%d",&a,&b,&c);
30             if(c==0)continue;
31             if(bl[a]==bl[b]){down(bl[a]);for(int i=a;i<=b;++i)da[i].v+=c,tms[i]++;up(bl[a]);}
32             else{
33                 down(bl[a]);for(int i=a;i<=cr[bl[a]];++i)da[i].v+=c,tms[i]++;up(bl[a]);
34                 down(bl[b]);for(int i=cl[bl[b]];i<=b;++i)da[i].v+=c,tms[i]++;up(bl[b]);
35                 for(int i=bl[a]+1;i<bl[b];++i)lz[i]+=c,mx[i]+=c,lzt[cr[i]]++;
36             }
37         }
38         if(s[0]=='M'){
39             scanf("%d%d%d",&a,&b,&c);
40             if(bl[a]==bl[b]){down(bl[a]);for(int i=a;i<=b;++i)if(da[i].v<c)da[i].v=c,tms[i]++;up(bl[a]);}
41             else{
42                 down(bl[a]);for(int i=a;i<=cr[bl[a]];++i)if(da[i].v<c)da[i].v=c,tms[i]++;up(bl[a]);
43                 down(bl[b]);for(int i=cl[bl[b]];i<=b;++i)if(da[i].v<c)da[i].v=c,tms[i]++;up(bl[b]);
44                 for(int i=bl[a]+1;i<bl[b];++i){
45                     if(c<=mx[i])continue;
46                     while(pt[i]<cr[i]&&lz[i]+da[pt[i]+1].v<c)pt[i]++;
47                     if(lz[i]+da[pt[i]].v>=c)continue;
48                     lzt[pt[i]]++;mx[i]=c;
49                 }
50             }
51         }
52         if(s[0]=='Q')scanf("%d",&a),down(bl[a]),printf("%lld %d
",da[a].v,tms[a]),up(bl[a]);
53     }
54 }
View Code

T2:旅行计划

题意:无向图多次询问两点之间在模意义下的最短路(询问间模数可能不同)$n,m,q le 10^5$

子任务:$k$为奇数/$k=2,w=1$

其实这两档部分分给的挺好的,但是想到正解仍然不容易。

k为奇数时你可以在同一条边来回走直到刷出你想要的数,所以只要联通答案就是$0$

对于后者,奇偶染色判奇环,如果联通块内存在奇环一定可以刷出想要的余数,否则就看两个点颜色是否相同。

对于其它子任务,因为要取模和环的问题所以考场上我想到了要gcd,然后就想不下去了。

事实上我们预处理把每个联通块里的边都除以边权的gcd再奇偶染色。

询问时,如果模数中2的指数少于边的gcd中的2的指数,那么余数一定是0。

否则如果k是奇数也可以刷出想要的数。

否则如果有奇环也一定能刷出来。

再否则如果染色相同那么也就不用刷了最短路模完就是0了。

再否则,我们也可以通过刷边的方法,将答案最小化为$gcd(k,gcd(edge))$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 200005
 4 int n,m,q,fir[S],l[S],to[S],v[S],bl[S],co[S],y[S],tim,g,ans,k,ec,eg[S];
 5 void link(int a,int b,int c){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=c;}
 6 void paint(int p,int c){
 7     bl[p]=tim;co[p]=c;
 8     for(int i=fir[p];i;i=l[i])if(!co[to[i]])paint(to[i],(co[p]+v[i]&1)+2);
 9         else if((co[p]+v[i]&1)+2!=co[to[i]])y[tim]=1;
10 }
11 int gcd(int a,int b){return b?gcd(b,a%b):a;}
12 int main(){
13     scanf("%d%d%d",&n,&m,&q);
14     for(int i=1,a,b,c;i<=m;++i)scanf("%d%d%d",&a,&b,&c),link(a,b,c),link(b,a,c);
15     for(int i=1;i<=n;++i)if(!co[i])tim++,paint(i,2);
16     for(int i=1;i<=n;++i)for(int j=fir[i];j;j=l[j])eg[bl[i]]=gcd(eg[bl[i]],v[j]);
17     for(int i=1;i<=tim;++i)y[i]=0;
18     for(int i=1;i<=n;++i)for(int j=fir[i];j;j=l[j])v[j]/=eg[bl[i]];
19     for(int i=1;i<=n;++i)co[i]=0;tim=0;
20     for(int i=1;i<=n;++i)if(!co[i])tim++,paint(i,2);
21     while(q-->0){
22         int x;scanf("%d%d%d",&x,&g,&k);
23         if(bl[x]!=bl[g])puts("NIE");
24         else printf("%d
",eg[bl[x]]/gcd(k,eg[bl[x]])%2==0||k&1||y[bl[x]]||co[x]==co[g]?0:gcd(k,eg[bl[x]]));
25     }
26 }
View Code

T3:Hack

题意:有向联通图,选定边集使所有1到n的路径均经过边集中的边合计恰好一次,最小化边权和。$nle100,m le 2500$

模型很像最小割。但是最小割并没有保证恰好经过一次。

经过多于一次的方案就是走某些路径由最小割后$T$点集走回了$S$点集。

于是我们把反向边开成$inf$就好了。这样就保证了即使它可以往回走的路径也被割断。

然而没必要$tarjan$缩点。直接按照含义跑,最后最小割是0表示不联通,无解。最小割$inf$表示都至少经过两次,也无解。

判掉之后你的网络流图上反向边$inf$成环所以你一定不割环上边所以是对的。$miku$大神实测可以$AC$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 1234567
 4 int fir[S],l[S],to[S],ec,v[S],dfn[S],low[S],tim,in[S],s[S],tp,bl[S],scc,n,m;
 5 void link(int a,int b,int w){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=w;}
 6 void tarjan(int p){
 7     dfn[p]=low[p]=++tim;s[++tp]=p;in[p]=1;
 8     for(int i=fir[p];i;i=l[i])if(!dfn[to[i]])tarjan(to[i]),low[p]=min(low[p],low[to[i]]);
 9         else if(in[to[i]])low[p]=min(low[p],dfn[to[i]]);
10     if(dfn[p]==low[p]){++scc;while(in[p])in[s[tp]]=0,bl[s[tp]]=scc,tp--;}
11 }
12 int Fir[S],L[S],To[S],q[S],Ec=1,al[S],d[S];long long ans,V[S];
13 void Link(int a,int b,int w){
14     L[++Ec]=Fir[a];Fir[a]=Ec;To[Ec]=b;V[Ec]=w;
15     L[++Ec]=Fir[b];Fir[b]=Ec;To[Ec]=a;V[Ec]=123456789012345;
16 }
17 bool SPFA(){
18     for(int i=1;i<=scc;++i)d[i]=123456789;d[bl[1]]=0;q[1]=bl[1];
19     for(int h=1,t=1;h<=t;++h)for(int i=Fir[q[h]];i;i=L[i])if(V[i]&&d[q[h]]+1<d[To[i]])
20         d[q[++t]=To[i]]=d[q[h]]+1;
21     return d[bl[n]]!=123456789;
22 }
23 long long dinic(int p,long long flow){//cout<<p<<' '<<flow<<endl;
24     long long r=flow;
25     if(p==bl[n])return flow;
26     for(int i=Fir[p];i&&r;i=L[i])if(d[To[i]]==d[p]+1&&V[i]){
27         long long x=dinic(To[i],min(r,V[i]));
28         if(!x)d[To[i]]=-1;
29         V[i]-=x;V[i^1]+=x;r-=x;
30     }return flow-r;
31 }
32 int main(){//freopen("3.in","r",stdin);freopen("3.out","w",stdout);
33     scanf("%d%d",&n,&m);
34     for(int i=1,a,b,w;i<=m;++i)scanf("%d%d%d",&a,&b,&w),link(a+1,b+1,w);
35     tarjan(1);
36     if(bl[1]==bl[n]||!bl[n])return puts("-1"),0;
37     for(int i=1;i<=n;++i)for(int j=fir[i];j;j=l[j])if(bl[i]&&bl[to[j]]&&bl[to[j]]!=bl[i])Link(bl[i],bl[to[j]],v[j]);
38     while(SPFA())ans+=dinic(bl[1],123456789012345);
39     printf("%lld
",ans);
40 }
View Code

.

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