[考试反思]0330省选模拟58:运作

百年难得一遇

其实只是$gmk$大神没写快速乘被卡常掉了$40pts$而已。

$T1$写了个挺简单但是貌似容易被遗忘的暴力。写了快速乘拿到了$60$

然后$T2$的简单$dp$也没啥脑子。

然后我这场干啥了??

其实我把主要的时间砸在$T3$上了,然而有一个细节想简单了,爆炸成$10$分了。

早知道写$50pts$的暴力了,已经想到了但是最后还是想写”正解“。其实$50pts$也不少哈(苟就是了)

三道题尝试写正解的都挂了没看见嘛?想啥呢

但是这套题的确挺难写的,代码长度都是$\geq 2.3k$。写着就很难受。

考察的思路还不错(至少我想着挺顺的

 

T1:带权图

大意:无向联通图,边带权$A,B,C$。$A(u,v)=-A(v,u),B(u,v)=B(v,u),C(u,v)=-C(v,u),\sum\limits_{v\in suc(u)} C(u,v)=0,\sum\limits_{(u,v) \in cycle(x)} B(u,v)C(u,v)-A(u,v)=0$。

以上运算均在$mod \ p$意义下。$p$是质数。$n\le 100,m\le 2000,P \le 10^{18}$。保证有唯一解

这么大的模数,那就快速乘呗。慢速乘多个$log$直接掉一档分。

然后最粗暴的想法(还是别太粗暴)就是列关于$C$的方程。

首先对于第二个条件可以把所有的点拿出来列方程。然而不是$n$个而是$n-1$个。(因为和都是$0$所以要去掉一个点)

然后随便跑一个生成树出来。

然后对于每条非树边,它与树边会形成一个环,根据这个环拿第三个条件来列式。一共$m-(n-1)$条非树边,也就有这么多方程。

所以总共就有了$m$个方程,高斯消元解出来就行了。$O(m^3)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 ll mod,g[2222][2222],a[6666],b[6666];
 5 int n,m,mp[111][111],fir[111],l[6666],to[6666],cnt,ec=1,f[111],al[111],fe[111];
 6 ll mul(ll a,ll b){return ((ll)((unsigned ll)a*b-(unsigned ll)(1.L*a/mod*b)*mod)+mod)%mod;}
 7 ll qp(ll b,ll t,ll a=1){for(;t;t>>=1,b=mul(b,b))if(t&1)a=mul(a,b);return a;}
 8 ll mo(ll a){return a>=mod?a-mod:a;}
 9 void Gauss(){
10     for(int i=1;i<=m;++i){
11         for(int j=i+1;!g[i][i];++j)for(int k=i;k<=m+1;++k)swap(g[i][k],g[j][k]);
12         ll iv=qp(g[i][i],mod-2);
13         for(int j=i;j<=m+1;++j)g[i][j]=mul(g[i][j],iv);
14         for(int j=1;j<=m;++j)if(j!=i)for(int k=m+1;k>=i;--k)g[j][k]=mo(g[j][k]-mul(g[i][k],g[j][i])+mod);
15     }for(int i=1;i<=m;++i)printf("%lld\n",g[i][m+1]);
16 }
17 void ctb(int i){g[cnt][i>>1]=i&1?mod-b[i>>1]:b[i>>1]; g[cnt][m+1]=mo(g[cnt][m+1]+(i&1?mod-a[i>>1]:a[i>>1]));}
18 void dfs(int p,int fa){
19     al[p]=2;
20     for(int i=fir[p];i;i=l[i])if(!al[to[i]])fe[to[i]]=i,dfs(to[i],p);
21         else if(al[to[i]]==2&&to[i]!=fa){
22             cnt++; ctb(i); int r=p;
23             while(r!=to[i])ctb(fe[r]),r=to[fe[r]^1];
24         }
25     al[p]=1;
26 }
27 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;}
28 int main(){//freopen("0.in","r",stdin);
29     cin>>n>>m>>mod;ll A,B;
30     for(int i=1,x,y;i<=m;++i)scanf("%d%d%lld%lld",&x,&y,&a[i],&b[i]),link(x,y),link(y,x);
31     for(int i=1;i<n;++i){
32         cnt++;
33         for(int j=fir[i];j;j=l[j])g[cnt][j>>1]=j&1?mod-1:1;
34     }dfs(1,0); Gauss();
35 }
60pts

不难发现最后那$m-(n-1)$个方程中,每个方程都包含恰好一条非树边$E$。想到主元法。

于是我们其实可以通过移项,两边同乘,得到$C_E=xC_i+yC_j+...+a$。这样的式子。右边是树边以及常量。

然后我们把所有的非树边都这么带入前$n-1$个式子,就的到了只关于树边的方程组。解出来再回代,复杂度是$O(n^3)$的。

正常主元法的题其实是$O(n^2m)$的。瓶颈在于将$m-n$个式子代入前$n$个里。

然而这道题每条非树边只会被两个节点的方程包含,所以是$O(nm)$的。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 ll mod,g[111][111],a[6666],b[6666],G[2222][2222],ans[2222];
 5 int n,m,fir[111],l[6666],to[6666],cnt,ec=1,f[111],al[111],fe[111],ex[2222],rex[111];
 6 ll mul(ll a,ll b){return ((ll)((unsigned ll)a*b-(unsigned ll)(1.L*a/mod*b)*mod)+mod)%mod;}
 7 ll qp(ll b,ll t,ll a=1){for(;t;t>>=1,b=mul(b,b))if(t&1)a=mul(a,b);return a;}
 8 ll mo(ll a){return a>=mod?a-mod:a;}
 9 void Gauss(){
10     for(int i=1;i<n;++i){
11         for(int j=i+1;!g[i][i];++j)for(int k=i;k<=n;++k)swap(g[i][k],g[j][k]);
12         ll iv=qp(g[i][i],mod-2);
13         for(int j=i;j<=n;++j)g[i][j]=mul(g[i][j],iv);
14         for(int j=1;j<n;++j)if(j!=i)for(int k=n;k>=i;--k)g[j][k]=mo(g[j][k]-mul(g[i][k],g[j][i])+mod);
15     }
16 }
17 void ctb(int i){G[cnt][i>>1]=i&1?mod-b[i>>1]:b[i>>1]; G[cnt][m+1]=mo(G[cnt][m+1]+(i&1?mod-a[i>>1]:a[i>>1]));}
18 void dfs(int p,int fa){
19     al[p]=2;
20     for(int i=fir[p];i;i=l[i])if(!al[to[i]])fe[to[i]]=i,dfs(to[i],p);
21         else if(al[to[i]]==2&&to[i]!=fa){
22             cnt++; ctb(i); int r=p; ex[i>>1]=-cnt;
23             while(r!=to[i])ctb(fe[r]),r=to[fe[r]^1];
24             ll iv=mod-qp(G[cnt][i>>1],mod-2);
25             for(int j=1;j<=m+1;++j)G[cnt][j]=mul(G[cnt][j],iv);
26         }
27     al[p]=1;
28 }
29 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;}
30 int main(){//freopen("0.in","r",stdin);
31     cin>>n>>m>>mod;ll A,B;
32     for(int i=1,x,y;i<=m;++i)scanf("%d%d%lld%lld",&x,&y,&a[i],&b[i]),link(x,y),link(y,x);
33     for(int i=1;i<n;++i){
34         cnt++;
35         for(int j=fir[i];j;j=l[j])G[cnt][j>>1]=j&1?mod-1:1;
36     }dfs(1,0);
37     int z=0;
38     for(int i=1;i<=m;++i)if(!ex[i])ex[i]=++z,rex[z]=i;
39     //for(int i=1;i<=m;++i)cout<<ex[i]<<endl;
40     //for(int i=1;i<=m;++i,puts(""))for(int j=1;j<=m+1;++j)printf("%2lld ",G[i][j]);
41     
42     for(int i=1;i<n;++i){
43         g[i][n]=G[i][m+1];
44         for(int j=1;j<=m;++j)if(ex[j]>0)g[i][ex[j]]=mo(g[i][ex[j]]+G[i][j]);
45             else if(G[i][j]){
46                 int p=-ex[j];
47                 for(int k=1;k<=m;++k)if(ex[k]>0)g[i][ex[k]]=mo(g[i][ex[k]]+mul(G[i][j],G[p][k]));
48                 g[i][n]=mo(g[i][n]+mul(G[i][j],G[p][m+1]));
49             }
50     }
51     //puts("");
52     //for(int i=1;i<=m;++i,puts(""))for(int j=1;j<=m+1;++j)printf("%2lld ",g[i][j]);
53     
54     Gauss();
55     
56     for(int i=1;i<n;++i)ans[rex[i]]=g[i][n];
57     for(int i=1;i<=m;++i)if(ex[i]<0){
58         int p=-ex[i]; ans[i]=mod-G[p][m+1];
59         for(int k=1;k<=m;++k)if(ex[k]>0)ans[i]=mo(ans[i]+mul(ans[k],G[p][k]));
60     }
61     
62     for(int i=1;i<=m;++i)printf("%lld\n",ans[i]);
63 }
View Code

 

T2:网络

大意:有$C$关键点。从$(1,1)$出发,沿两条不相交的路径向右或向下走到$(n,m)$,且经过关键点数不超过$D$。答案对$mod$取模。求方案数。$C \le 200,n,m \le 10^5,mod \le 10^9$

一看这个模数就知道它大概想让你干什么。$CRT$呗。

然而特殊的是,这道题$n,m$不大,所以可以直接预处理阶乘,逆元,以及阶乘所含的质数的次数。

如果只有一条路径,可以直接$dp:f[i][j]$表示踩了$i$个关键点,最后落在第$j$个上。$g[i][j]$表示从第$i$个关键点走到第$j$个,不经过其它关键点。

$g$可以通过枚举“过程中最后一个遇到的关键点”来进行容斥。有了$g$,$f$很好转移。复杂度$O(C^3)$

如果不考虑关键点,那么考虑两条路径相交的情况。

其实本质上是两条$(2,1)\rightarrow (n,m-1),(1,2) \rightarrow (n-1,m)$的路径。如果相交,那么交点一定有偶数个。(碰上就立刻分开算两个)

在每个交点处,两条路径可以选择交换上下关系,也可以不交换。

如果交换奇数次那么最后的路径其实是$(1,2)\rightarrow (n,m-1),(2,1) \rightarrow (n-1,m)$。

类似状压的角度考虑,发现对于两条特定的相交路径,交换奇数次的方案数与交换偶数次的相等。

然而如果两条路径压根就不相交,那么当然会被记录到偶数中去。

那么答案就是$(2,1)\rightarrow (n,m-1),(1,2) \rightarrow (n-1,m)$的方案数减去$(1,2)\rightarrow (n,m-1),(2,1) \rightarrow (n-1,m)$的方案数。

这样就去掉了相交的限制。

外面再套一层$CRT$就完事了。然而可能会卡常。

发现再对于同一个质数$4种起终点对关系,$g$数组大概都是一样的,只有$g[0][i],g[i][C+1]$变化了。所以不每次更新,就卡过去了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 222222
 4 struct Pt{int x,y;friend bool operator<(Pt x,Pt y){return x.x+x.y<y.x+y.y;}}P[222];
 5 int c,d,n,m,fac[S],ft[S],finv[S],g[222][222],f[222][222],A[222],B[222],p,k,pk,pw[33];
 6 void exgcd(int a,int b,int&x,int&y){
 7     if(!b){x=1;y=0;return;}
 8     exgcd(b,a%b,y,x);y-=a/b*x;
 9 }
10 int mo(int x){return x>=pk?x-pk:x;}
11 int inv(int a,int b){int x,y;pk=b;exgcd(a,b,x,y);return mo(x%pk+pk);}
12 int C(int b,int t,int a=1){return (b<t||t<0)||ft[b]-ft[t]-ft[b-t]>=k?0:1ll*pw[ft[b]-ft[t]-ft[b-t]]*fac[b]%pk*finv[t]%pk*finv[b-t]%pk;}
13 int cal(int i,int j){return C(P[j].x+P[j].y-P[i].x-P[i].y,P[j].x-P[i].x);}
14 void cal(int*a){
15     for(int i=0,j=i+1;j<=c;++j){
16         g[i][j]=cal(i,j);
17         for(int k=i+1;k<j;++k)g[i][j]=mo(g[i][j]-1ll*g[i][k]*cal(k,j)%pk+pk);
18     }
19     for(int i=0,j=c;i<j;++i){
20         g[i][j]=cal(i,j);
21         for(int k=i+1;k<j;++k)g[i][j]=mo(g[i][j]-1ll*g[i][k]*cal(k,j)%pk+pk);
22     }
23     for(int i=1;i<c;++i)if(P[i].x==P[0].x&&P[i].y==P[0].y){for(int j=1;j<=c;++j)g[0][j]=0;g[0][i]=1;}
24     for(int i=1;i<c;++i)if(P[i].x==P[c].x&&P[i].y==P[c].y){for(int j=1;j<=c;++j)g[j][c]=0;g[i][c]=1;}
25     for(int i=0;i<=c;++i)for(int j=0;j<=d;++j)f[i][j]=0; f[0][0]=1;
26     for(int j=1;j<=c;++j)for(int k=0;k<j;++k)if(g[k][j])for(int i=0;i<d;++i)f[j][i+1]=(f[j][i+1]+1ll*f[k][i]*g[k][j])%pk;
27     for(int i=0;i<=d;++i)a[i]=f[c][i];
28 }
29 int solve(){
30     fac[0]=pw[0]=finv[0]=1; int a=0;
31     for(int i=1;i<n+m;++i){
32         int x=i; ft[i]=ft[i-1];
33         while(x%p==0)ft[i]++,x/=p;
34         fac[i]=1ll*fac[i-1]*x%pk;
35         finv[i]=inv(fac[i],pk);
36     }for(int i=1;i<k;++i)pw[i]=pw[i-1]*p;
37     for(int i=1;i<c;++i)for(int j=i+1;j<c;++j){
38         g[i][j]=cal(i,j);
39         for(int k=i+1;k<j;++k)g[i][j]=mo(g[i][j]-1ll*g[i][k]*cal(k,j)%pk+pk);
40     }
41     P[0]=(Pt){1,2};P[c]=(Pt){n-1,m};cal(A);
42     P[0]=(Pt){2,1};P[c]=(Pt){n,m-1};cal(B);
43     for(int i=0;i<=d;++i)for(int j=0;i+j<=d;++j)a=(a+1ll*A[i]*B[j])%pk;
44     P[0]=(Pt){2,1};P[c]=(Pt){n-1,m};cal(A);
45     P[0]=(Pt){1,2};P[c]=(Pt){n,m-1};cal(B);
46     for(int i=0;i<=d;++i)for(int j=0;i+j<=d;++j)a=mo(a+pk-1ll*A[i]*B[j]%pk);
47     return a;
48 }
49 int main(){//freopen("grid20.in","r",stdin);
50     int T,Pr[9],K[9],ans[9],PK[9],z,mod;cin>>T;while(T--){
51         cin>>n>>m>>c>>d>>mod; 
52         for(int i=1;i<=c;++i)cin>>P[i].x>>P[i].y;
53         sort(P+1,P+1+c); c++;d+=2;z=0;
54         for(int i=2;i*i<=mod;++i)if(mod%i==0){
55             Pr[++z]=i;K[z]=0;PK[z]=1;
56             while(mod%i==0)mod/=i,K[z]++,PK[z]*=i;
57         }if(mod!=1)Pr[++z]=mod,K[z]=1,PK[z]=Pr[z];
58         for(int i=1;i<=z;++i)p=Pr[i],k=K[i],pk=PK[i],ans[i]=solve();
59         for(int i=2,M;i<=z;++i)
60             M=PK[i-1]*PK[i],
61             ans[i]=(1ll*ans[i-1]*PK[i]%M*inv(PK[i],PK[i-1])+1ll*ans[i]*PK[i-1]%M*inv(PK[i-1],PK[i]))%M,
62             PK[i]*=PK[i-1];
63         cout<<ans[z]<<endl;
64     }
65 }
View Code

 

T3:修路

大意:图,按照一定规则生成了一棵最短路树。强制在线,多次询问:给定祖先后代$(u,v)$,这条链从上往下最长可以砍掉多少边,使得砍掉之后根到$v$最短距离不变。$n,m,q \le 2\itmes 10^5$

题意就是求出一个深度最大的点$w$,满足在反向的最短路径有向图上,它可以不经过$(u...w)$的树边,到达深度$\le dep(u)$的点。

我们设$C_x$表示点$x$在不经过$(C_x,x)$的树边的情况下可以到达的点中能到达的最小深度。

假如我们求出这个,那么我们就可以对于每个询问直接倍增从$v$开始往上跳了。

发现每条非树边(u,v)的贡献,就是$C_v$对$C_{lca(u,v)...u}$取$min$。拿树剖线段树维护一下就行。

然后只要把所有点按照到根的距离排序,依次考虑就能保证更新出的$C$是正确的了(因为小点更新大点,差不多就是拓扑的意思)

不倍增的话也可以直接线段树上二分。常数可能会比较大。

另一种做法是主席树,以深度为下标,也是维护能到达的最小深度,然后主席树上二分。

复杂度大约都是$O(nlog^2n+qlogn)$级别的。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int read(){int p=0;char ch=getchar();
 4     for(;!isdigit(ch);ch=getchar()); for(;isdigit(ch);ch=getchar())p=p*10+ch-48;
 5     return p;
 6 }
 7 #define S 222222
 8 priority_queue<pair<long long,int> >q;
 9 vector<int>to[S],w[S],s[S];
10 int n,m,o,C,p[S],f[20][S],rp[S],al[S],dep[S],low[20][S],la,sz[S],hson[S],tp[S],dfn[S],tim,idfn[S],v[S<<2],rs[S];
11 long long d[S];
12 void con(int a,int b,int W){
13     to[a].push_back(b);w[a].push_back(W);
14     to[b].push_back(a);w[b].push_back(W);
15 }
16 void dfs(int p){
17     dep[p]=low[0][p]=dep[f[0][p]]+1; sz[p]=1;
18     for(int i=0,y;y=i<w[p].size()?to[p][i]:0;++i)if(d[y]==d[p]+w[p][i]&&!al[y]){
19         s[p].push_back(y),al[y]=1,f[0][y]=p,dfs(y);sz[p]+=sz[y];
20         if(sz[y]>sz[hson[p]])hson[p]=y;
21     }
22 }
23 void DFS(int p,int top){
24     dfn[p]=++tim; idfn[tim]=p; tp[p]=top;
25     for(int i=1;i<19;++i)f[i][p]=f[i-1][f[i-1][p]];
26     if(hson[p])DFS(hson[p],top);
27     for(int i=0;i<s[p].size();++i)if(s[p][i]!=hson[p])DFS(s[p][i],s[p][i]);
28 }
29 #define lc p<<1
30 #define rc lc|1
31 #define md (L+R>>1)
32 void build(int p,int L,int R){
33     if(L==R){v[p]=low[0][idfn[L]];return;}
34     build(lc,L,md); build(rc,md+1,R); v[p]=min(v[lc],v[rc]);
35 }
36 int ask(int l,int r,int p=1,int L=1,int R=n){
37     if(l<=L&&R<=r)return v[p];
38     return min(l<=md?ask(l,r,lc,L,md):n,r>md?ask(l,r,rc,md+1,R):n);
39 }
40 void chg(int P,int w,int p=1,int L=1,int R=n){
41     if(L==R){v[p]=w;return;}
42     if(P<=md)chg(P,w,lc,L,md);else chg(P,w,rc,md+1,R); v[p]=min(v[lc],v[rc]);
43 }
44 int query(int x,int y,int a=n){
45     while(tp[x]!=tp[y])if(dep[tp[x]]>dep[tp[y]])x=f[0][tp[x]];
46         else a=min(a,ask(dfn[tp[y]],dfn[y])),y=f[0][tp[y]];
47     return min(a,ask(min(dfn[x],dfn[y]),dfn[y]));
48 }
49 int main(){//freopen("0.in","r",stdin);
50     cin>>n>>m>>C>>o;
51     for(int i=1;i<=n;++i)p[i]=read(),rp[p[i]]=i;
52     for(int i=1,a,b;i<=m;++i)a=read(),b=read(),con(a,b,read());
53     for(int i=1;i<=n;++i){
54         for(int j=0;j<w[i].size();++j)q.push(make_pair(-p[to[i][j]],w[i][j]));
55         to[i].clear(); w[i].clear();
56         while(!q.empty())to[i].push_back(rp[-q.top().first]),w[i].push_back(q.top().second),q.pop();
57     }
58     memset(d,0x3f,sizeof d); d[C]=0; q.push(make_pair(0,C));
59     while(!q.empty()){
60         long long D=-q.top().first;int p=q.top().second; q.pop();
61         if(D!=d[p])continue;
62         for(int i=0;i<w[p].size();++i) if(d[to[p][i]]>d[p]+w[p][i])
63             q.push(make_pair(-(d[to[p][i]]=d[p]+w[p][i]),to[p][i]));
64     }
65     dfs(C); DFS(C,C); build(1,1,n);
66     for(int i=1;i<=n;++i)rs[i]=i;
67     sort(rs+1,rs+1+n,[](int a,int b){return d[a]<d[b];});
68     for(int _=1,x;x=rs[_];++_){
69         for(int i=0,y;y=i<w[x].size()?to[x][i]:0;++i)if(d[x]==d[y]+w[x][i]&&y!=f[0][x])
70             chg(dfn[x],low[0][x]=min(low[0][x],query(x,y)));
71         for(int i=1;i<19;++i)low[i][x]=min(low[i-1][x],low[i-1][f[i-1][x]]);
72     }
73     for(int i=read(),u,v;i;--i){
74         u=read();v=read(); if(o)u^=la,v^=la; la=0;
75         for(int i=18;~i;--i)if(low[i][v]>dep[u])v=f[i][v];
76         printf("%d\n",la=dep[v]-dep[u]);
77     }
78 }
View Code
posted @ 2020-03-30 22:35  DeepinC  阅读(213)  评论(0编辑  收藏  举报