1 条题解
-
0
练差分约束的时候遇到的好题。
题意简述
个数,假设第 个数为 。给定 条形如 关系,以及 条形如 的关系。求这 个 中最多有几种不同的 。
思路
这种形式就很像差分约束,所以也不过多阐述为什么要用差分约束。
首先,不难看出建边会有三种边权分别是 而且只会出现这三种。继续考虑,受到这个题的启发,想到了用 Tarjan 先缩点,在每一个强联通分量内,每两个点都可以互相到达,那么就可以轻易算出负环和正环,而且还能得出一个强联通分量内 的种类数。
之所以能求出这个种类数的原因,是因为我们能算出一个强联通分量的 的极差,又因为边权只有三种,则在最大值与最小值之间的每一个就都能取到。
然后注意到数据范围,应该是给 Floyd 算法用的,
但这个时候我的 SPFA 板子都打好了,这就使之前一直只写 SPFA 的我沉思片刻。其实差分约束的实质是利用最短路中是否有负环,或者最长路中是否有正环来检验约束条件是否能够成立。但显然如果用 Floyd 直接求出全源最短路或最长路,自然也可以达到所需的效果。
继续看数据范围,就会发现大概率是存在重边的。那么建边的时候就要注意取最值。本题解是用最短路写的,因此取最小值,同时存在负环时就判断无解。
通过之前的推断,不同强联通分量之间互不影响,因此我们对每一个强联通分量求出极差,不过显然由于左右端点能取到,那么这个强联通分量对答案的贡献就是极差加一。最后将每个强联通分量的贡献求和即可。
那么极差又应该怎么求呢?显然可以通过强联通分量中每两点间距离的最大值求出,显然距离最远的两个点就是左右端点。因此我们跑 Floyd 就只用在每一个强联通分量中跑了。
总结一下整体过程:
- 求强联通分量,缩点;
- 在每个强联通分量内跑全源最短路;
- 在每个强联通分量求每两点之间距离的最大值,这个值加一就是对答案的贡献;
- 对每个强联通分量的贡献求和。
做完了。
代码
#include<bits/stdc++.h> #define ll long long #define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0) using namespace std; const int N=605; const int inf=0x3f3f3f3f; int n,m1,m2,dis[N][N]; int ans,f[N]; // 答案与强联通分量的贡献,即极差 +1 void add(int u,int v,int w){dis[u][v]=min(dis[u][v],w);} // Tarjan 部分 int dfn[N],low[N],idx; stack<int>st; bool ins[N]; int col[N],color; // 新图,color 是强联通分量数 void tarjan(int u){ dfn[u]=low[u]=++idx; st.push(u),ins[u]=1; for(int v=1;v<=n;v++){ if(dis[u][v]==inf)continue; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(ins[v]){ low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u]){ color++; int v; do{ v=st.top(),st.pop(); ins[v]=0; col[v]=color; }while(u!=v); } return; } int main(){ ios;cin>>n>>m1>>m2; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i==j)dis[i][j]=0; else dis[i][j]=inf; } } // 建图 int u,v; for(int i=1;i<=m1;i++){ // u=v-1 cin>>u>>v; add(u,v,1); // v<=u+1 ,原形是 u>=v-1 add(v,u,-1); // u<=v+(-1) ,原形是 u<=v-1 } for(int i=1;i<=m2;i++){ // u<=v cin>>u>>v; add(v,u,0); // u<=v+0 } for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i); // Floyd 部分 for(int k=1;k<=n;k++){ for(int i=1;i<=n;i++){ if(col[i]!=col[k]||dis[i][k]==inf)continue; for(int j=1;j<=n;j++){ if(col[i]==col[j])dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); } } } // 统计贡献 for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i==j&&dis[i][j]<0){ cout<<"NIE\n"; return 0; } if(col[i]==col[j]){ int p=col[i]; f[p]=max(f[p],dis[i][j]+1); } } } for(int i=1;i<=n;i++)ans+=f[i]; cout<<ans<<'\n'; return 0; }完结撒花花!
- 1
信息
- ID
- 4453
- 时间
- 7500ms
- 内存
- 64MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者