[考試反思]1031csp-s模擬測試96:常態


按照smily的說法這一場的大眾分暴力分是不是265啊QwQ那我可真是個大垃圾

總算還是回歸了常態。

T3文件名寫錯,把“city.in”寫成“city,in”

還好,只丟了20分。

T2亂打$O(n^2 \ log^2n)$數據水拿了$95pts$

T1慢速乘yy一下就沒了。

然后其實很垃圾。特別困,狀態很差,腦子也動不起來了。

於是一直在打。打完暴力打對拍,偶爾停下來想一想。

T2想到了正解但是不會證復雜度所以沒有打(這個倍增思路被我在考場上YY出來很多次但是一直覺得它的復雜度不對所以沒打過。。。)

但是當我在9:35掛上T2的對拍發現我RE不輸出的時候我就是到事情不太妙。(考后看到交上去的代碼WA 0了)

這次考試不是210分鍾而是205分鍾所以更加緊迫。趕忙gdb發現是剪枝掛了,干掉剪枝就沒事了。

對拍很重要啊!

 

T1:求和

算是個慢速乘板子。快速乘據說會炸精。

 1 #include<cstdio>
 2 #define LL long long
 3 LL mod,a,b,c,d;
 4 LL mult(LL b,LL t,LL a=0){
 5     b%=mod;
 6     for(;t;t>>=1,b+=b){
 7         if(b>=mod)b-=mod;
 8         if(t&1)a+=b;
 9         if(a>=mod)a-=mod;
10     }
11     return a;
12 }
13 int main(){
14     freopen("sum.in","r",stdin);
15     freopen("sum.out","w",stdout);
16     scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&mod);
17     LL X=c-a+1,Y=d-b+1,x=a+c,y=b+d,ans=0;
18     if(X&1)ans+=mult(mult(X,x>>1),Y);
19     else ans+=mult(mult(X>>1,x),Y);
20     if(Y&1)ans+=mult(mult(Y,y>>1),X);
21     else ans+=mult(mult(Y>>1,y),X);
22     ans-=mult(X,Y);
23     if(ans<0)ans+=mod;
24     if(ans>mod)ans-=mod;
25     printf("%lld\n",ans);
26 }
View Code

 

T2:分組配對

最有配對方案一定是最大的與最大的配,第二與第二配。

應該不需要證明吧,簡單的數學推導。

然后這是$O(n^2 \ log^2n)$的暴力

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 int n,a[500005],b[500005],ra[500005],rb[500005],ans,high[500005];
 5 long long M,sum[500005];
 6 bool chk(int l,int r){
 7     if(sum[r]-sum[l-1]>M)return false;
 8     for(int i=l;i<=r;++i)ra[i]=a[i],rb[i]=b[i];
 9     sort(ra+l,ra+r+1);sort(rb+l,rb+r+1);
10     long long A=0;
11     for(int i=l;i<=r;++i)A+=1ll*ra[i]*rb[i];
12     return A<=M;
13 }
14 int main(){
15     freopen("pair.in","r",stdin);
16     freopen("pair.out","w",stdout);
17     scanf("%d%lld",&n,&M);
18     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
19     for(int i=1;i<=n;++i)scanf("%d",&b[i]);
20     for(int i=1;i<=n;++i)sum[i]=sum[i-1]+1ll*a[i]*b[i];
21     for(int i=0;i<20;++i)for(int j=1<<i;j<1<<i+1&&j<=n;++j)high[j]=i;
22     for(int i=1;i<=n;){
23         int l=i,r=n;ans++;
24         while(l<r-1)if(chk(i,l+r>>1))l=l+r>>1;else r=(l+r>>1)-1;
25         if(chk(i,r))i=r+1;else i=l+1;
26     }printf("%d\n",ans);
27 }
View Code

實際上套一個先倍增后二分就能解決二分界過大的問題。

進階指南的原題,不細說(之前沒好好看書。。。)

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 int n,a[500005],b[500005],ra[500005],rb[500005],ans,high[500005];
 5 long long M,sum[500005];
 6 bool chk(int l,int r){
 7     if(sum[r]-sum[l-1]>M)return false;
 8     for(int i=l;i<=r;++i)ra[i]=a[i],rb[i]=b[i];
 9     sort(ra+l,ra+r+1);sort(rb+l,rb+r+1);
10     long long A=0;
11     for(int i=l;i<=r;++i)A+=1ll*ra[i]*rb[i];
12     return A<=M;
13 }
14 int main(){
15     freopen("pair.in","r",stdin);
16     freopen("pair.out","w",stdout);
17     scanf("%d%lld",&n,&M);
18     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
19     for(int i=1;i<=n;++i)scanf("%d",&b[i]);
20     for(int i=1;i<=n;++i)sum[i]=sum[i-1]+1ll*a[i]*b[i];
21     for(int i=1;i<=n;){
22         int l,r,j;ans++;
23         for(j=0;i+(1<<j)-1<=n;++j)if(!chk(i,i+(1<<j)-1)){l=i+(1<<j-1)-1;r=i+(1<<j)-2;break;}
24         if(i+(1<<j)-1>n)l=i+(1<<j-1)-1,r=n;
25         while(l<r-1)if(chk(i,l+r>>1))l=l+r>>1;else r=(l+r>>1)-1;
26         if(chk(i,r))i=r+1;else i=l+1;
27     }printf("%d\n",ans);
28 }
View Code

 

T3:城市游戲

神仙。難想。

抄題解就好了。

設$f_i$表示現在小B還沒有封路過,這時候小A在最優決策下走到n號點的距離。

考慮轉移,設$d_{i,j}$表示$i - j$這條路徑被封上之后,從i到n的最短路。

那么轉移的方程就是$f_i=min(max(f_j+dis_{i,j},d_{i,j}))$。$dis$是邊的長度。

注意這個$min$和$max$的嵌套,它的具體含義就是小B會在里面的兩種決策里選擇最優的,而小A在考慮所有小B的可能的決策后選擇最優的。

內層的具體含義就是如果現在立刻封路,那么現在小A的剩余距離就是$d_{i,j}$。剩下的一種決策就是不攔着小A讓他隨便繼續走,以后再封路。

$f_n=0$。倒着推回去就好了。具體做法就是$Dijkstra$。把$max$里面的東西當做邊權跑最短路。

因為是倒推,所以對$f$定義的理解也要倒着想。

現在沒有封路的話,那么就可以選擇兩種:

一種是立刻封掉下一條路,然后不再封路,一種是先不動讓小A繼續走,然后以后再封,取以后的最優決策$f_j$。

構造以n為根的最短路徑樹,這樣就能得到每個點到他的祖先點的距離。同時也就能得到每個點到n的距離。

所以就是從n號點再跑一個$Dijkstra$得到最短路,記錄前驅邊,用前驅邊建樹。

如果小B不封最短路樹上的邊,那么小A就會直接按照最短路樹上的邊走到n。

所以必須封一條最短路徑樹上的邊$(u,v)$。這么一封,樹就斷成了兩個聯通塊。

如果$u$是父親的話,那么兩個聯通塊一個是以$v$為根的子樹,另一個是原樹的剩余部分。

想要替代這條被封的邊的話,那么一定會走一條邊$(a,b)$且$a,b$分別屬於兩個聯通塊。

比較明顯啊,如果屬於同一個聯通塊那么$u,v$還是不聯通啊沒有替代原邊的效果。

替代之后,從$v$到$n$的最短距離就是$dt[a]+dt[b]+dis_{a,b}-dt[v]$。

其中$dt$表示每個節點到n號點的最短路。畫個圖就明白了不細說。

這樣的話我們就能得到$d$數組了。但是復雜度還是不對的。

換一個思路,我們考慮每一條樹外邊的貢獻。

它能讓什么樣的樹邊所斷開的聯通塊重新聯通呢?條件是$a \in subtree(v) \ and \ b \notin subtree(v)$

再進一步,當且僅當$v$在$a$或$b$的祖先鏈上(那么一個在$v$子樹里一個在子樹外)。

但又不能同時在$a,b$的祖先鏈上(那么就都在子樹外了)。

那么就是$lca$以下的祖先鏈部分了。

邊權下放到兒子,那么用兒子的權值代表父邊的話,就是$a$到$lca$和$b$到$lca$,不含$lca$

最后得到的就是$w_i=d_{fa_i,i}$

鏈上更新取$min$。可以用題解里說的神仙的並查集,但樹鏈剖分貌似更加顯然(復雜度都多了一個$log$)

然而復雜度什么的並不是問題,其實爆跳父親跳到$lca$不斷更新即可。

倆$Dijkstra$一個$dfs$。代碼還是挺好寫的。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define LL long long
 4 #define S 100005
 5 #define inf 100000000000000000
 6 priority_queue<pair<LL,int>,vector<pair<LL,int> >,greater<pair<LL,int> > >q;
 7 int fir[S],l[S<<2],to[S<<2],ec=1,n,m,Q[S],pre[S],FIR[S],L[S],TO[S],EC,f[18][S],dep[S];
 8 char al[S],iq[S],nt[S<<2],AL[S],it[S<<2];LL dt[S],ans,DT[S],w[S<<2],dp[S];
 9 void link(int a,int b,int v){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;w[ec]=v;}
10 void LINK(int a,int b){L[++EC]=FIR[a];FIR[a]=EC;TO[EC]=b;}
11 void dfs(int p){
12     for(int i=1;i<18;++i)f[i][p]=f[i-1][f[i-1][p]];dep[p]=dep[f[0][p]]+1;
13     for(int i=FIR[p];i;i=L[i])f[0][TO[i]]=p,dfs(TO[i]);
14 }
15 int lca(int a,int b){
16     int subdep=dep[a]-dep[b];
17     if(subdep<0)subdep*=-1,a^=b^=a^=b;
18     for(int i=17;~i;--i)if(subdep&1<<i)a=f[i][a];
19     if(a==b)return a;
20     for(int i=17;~i;--i)if(f[i][a]!=f[i][b])a=f[i][a],b=f[i][b];
21     return f[0][a];
22 }
23 int main(){
24     freopen("city.in","r",stdin);freopen("city.out","w",stdout);
25     scanf("%d%d",&n,&m);
26     for(int i=1,x,y,v;i<=m;++i)scanf("%d%d%d",&x,&y,&v),link(x,y,v),link(y,x,v);
27     for(int i=1;i<n;++i)DT[i]=dt[i]=dp[i]=inf;
28     q.push(make_pair(0,n));
29     while(!q.empty()){
30         int p=q.top().second; q.pop();
31         if(AL[p])continue;AL[p]=1;
32         for(int i=fir[p];i;i=l[i])if(DT[to[i]]>DT[p]+w[i])
33             pre[to[i]]=i,q.push(make_pair(DT[to[i]]=DT[p]+w[i],to[i]));
34     }
35     for(int i=1;i<n;++i)LINK(to[pre[i]^1],i),it[pre[i]]=it[pre[i]^1]=1;
36     dfs(n);
37     for(int i=1;i<=m;++i)if(!it[i<<1]){
38         int a=to[i<<1],b=to[i<<1|1],LCA=lca(a,b);LL val=DT[a]+DT[b]+w[i<<1];
39         while(a!=LCA)dp[a]=min(dp[a],val-DT[a]),a=f[0][a];
40         while(b!=LCA)dp[b]=min(dp[b],val-DT[b]),b=f[0][b];
41     }
42     q.push(make_pair(0,n));
43     while(!q.empty()){
44         int p=q.top().second; q.pop();
45         if(al[p])continue;al[p]=1;
46         for(int i=fir[p];i;i=l[i])if(dt[to[i]]>max(dt[p]+w[i],it[i]?dp[to[i]]:DT[to[i]]))
47             q.push(make_pair(dt[to[i]]=max(dt[p]+w[i],it[i]?dp[to[i]]:DT[to[i]]),to[i]));
48     }printf("%lld\n",dt[1]>=inf?-1:dt[1]);
49 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM