百年难得一遇
其实只是$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),sumlimits_{vin suc(u)} C(u,v)=0,sumlimits_{(u,v) in cycle(x)} B(u,v)C(u,v)-A(u,v)=0$。
以上运算均在$mod p$意义下。$p$是质数。$nle 100,mle 2000,P le 10^{18}$。保证有唯一解
这么大的模数,那就快速乘呗。慢速乘多个$log$直接掉一档分。
然后最粗暴的想法(还是别太粗暴)就是列关于$C$的方程。
首先对于第二个条件可以把所有的点拿出来列方程。然而不是$n$个而是$n-1$个。(因为和都是$0$所以要去掉一个点)
然后随便跑一个生成树出来。
然后对于每条非树边,它与树边会形成一个环,根据这个环拿第三个条件来列式。一共$m-(n-1)$条非树边,也就有这么多方程。
所以总共就有了$m$个方程,高斯消元解出来就行了。$O(m^3)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 ",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 }
不难发现最后那$m-(n-1)$个方程中,每个方程都包含恰好一条非树边$E$。想到主元法。
于是我们其实可以通过移项,两边同乘,得到$C_E=xC_i+yC_j+...+a$。这样的式子。右边是树边以及常量。
然后我们把所有的非树边都这么带入前$n-1$个式子,就的到了只关于树边的方程组。解出来再回代,复杂度是$O(n^3)$的。
正常主元法的题其实是$O(n^2m)$的。瓶颈在于将$m-n$个式子代入前$n$个里。
然而这道题每条非树边只会被两个节点的方程包含,所以是$O(nm)$的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 ",ans[i]); 63 }
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) ightarrow (n,m-1),(1,2) ightarrow (n-1,m)$的路径。如果相交,那么交点一定有偶数个。(碰上就立刻分开算两个)
在每个交点处,两条路径可以选择交换上下关系,也可以不交换。
如果交换奇数次那么最后的路径其实是$(1,2) ightarrow (n,m-1),(2,1) ightarrow (n-1,m)$。
类似状压的角度考虑,发现对于两条特定的相交路径,交换奇数次的方案数与交换偶数次的相等。
然而如果两条路径压根就不相交,那么当然会被记录到偶数中去。
那么答案就是$(2,1) ightarrow (n,m-1),(1,2) ightarrow (n-1,m)$的方案数减去$(1,2) ightarrow (n,m-1),(2,1) ightarrow (n-1,m)$的方案数。
这样就去掉了相交的限制。
外面再套一层$CRT$就完事了。然而可能会卡常。
发现再对于同一个质数$4种起终点对关系,$g$数组大概都是一样的,只有$g[0][i],g[i][C+1]$变化了。所以不每次更新,就卡过去了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
T3:修路
大意:图,按照一定规则生成了一棵最短路树。强制在线,多次询问:给定祖先后代$(u,v)$,这条链从上往下最长可以砍掉多少边,使得砍掉之后根到$v$最短距离不变。$n,m,q le 2itmes 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)$级别的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 ",la=dep[v]-dep[u]); 77 } 78 }