P3530 [POI2012]FES-Festival
题面
题意
在满足给定的条件下,求出赛车手的成绩可能的取值有多少种。
解析
差分约束
不等式关系自然联想到差分约束。
- 对于第一种不等式
因为给定了相差的值,所以建双向边:
\[dis[i] \leq dis[j] - 1
\]
\[dis[j] \leq dis[i] + 1
\]
通过两个不等式,锁定 \(i,j\) 之间的差值为 \(1\)。
- 对于第二种不等式
建单向边:
\[dis[i] \leq dis[j] + 0
\]
Floyd
容易发现求赛车手的成绩可能的取值有多少种,其实就是赛车手正整数值域大小的最大值,因为这道题的数据卡了 \(SPFA\),所以我们只好用 \(Floyd\),但是因为直接跑,\(n^3=216000000\),显然会炸,所以我们就想到了缩点。
缩点
容易发现,强联通分量之间是不会相互影响的,所以我们可以在强联通分量内进行 \(Floyd\),跑最短路后,找出最长路径,也就是最大的值域,而这个值域的大小 = R - L + 1;
缩点后的Floyd
设 \(dis[i][j]\) 表示从\(i\) 到 \(j\) 的最长距离。
在每个阶段开始时,判断进行状态转移的三个点是否在同一个连通块内。
- 对于合法的答案:
求出 \(dis\) 后,找出每个连通块内的最长路径 \(max_{i}\),根据上面的分析,每个连通块对答案的贡献为 \(max_{i} + 1\)。
所以最终的答案就是:
\[\sum_{i = 1} ^{cnt}max_{i} + 1
\]
- 对于不合法的答案:
如果存在负环,则无解。\(Floyd\) 判负环先初始化 \(dis[i][i] = 0\),如果存在负环,那么 \(dis[i][i]\) 的值就会被修改,只需要判断 \(dis[i][i]\) 的值是否为 \(0\)就行了。
代码
如果想不吸氧过的话,可以加 \(register\)。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 605;
struct edge {
int next,to;
}a[N << 1];
int head[N],dis[N][N],maxn[N],n,m1,m2,a_size = 1,ans = 0;
inline void add(int u,int v) {
a[++a_size] = (edge){head[u],v};
head[u] = a_size;
}
int dfn[N],low[N],sta[N],c[N],top = 0,cnt = 0,num = 0;
bool ins[N];
void tarjan(int x) {
dfn[x] = low[x] = ++num;
sta[++top] = x,ins[x] = true;
for(int i = head[x]; i; i = a[i].next) {
int y = a[i].to;
if(!dfn[y]) {
tarjan(y);
low[x] = min(low[x],low[y]);
}
else if(ins[y])
low[x] = min(low[x],dfn[y]);
}
if(dfn[x] == low[x]) {
cnt++; int y; do {
y = sta[top--]; ins[y] = false;
c[y] = cnt;
}while(y != x);
}
}
inline int read() {
int x = 0,flag = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')flag = -1;ch = getchar();}
while(ch >='0' && ch <='9'){x = (x << 3) + (x << 1) + ch - 48;ch = getchar();}
return x * flag;
}
int main() {
memset(dis,0x3f,sizeof(dis));
n = read(),m1 = read(),m2 = read();
for(int i = 1; i <= n; i++) dis[i][i] = 0;
for(int i = 1; i <= m1; i++) {
int u = read(),v = read();
add(u,v); add(v,u);
dis[u][v] = min(dis[u][v],-1);
dis[v][u] = min(dis[v][u],1);
}
for(int i = 1; i <= m2; i++) {
int u = read(),v = read();
add(u,v);
dis[u][v] = min(dis[u][v],0);
}
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i);
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
if(c[i] != c[k]) continue;
for(int j = 1; j <= n; j++) {
if(c[j] != c[i]) continue;
dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
}
}
}
for(int i = 1; i <= n; i++)
if(dis[i][i]) {
puts("NIE");
return 0;
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(c[j] != c[i]) continue;
maxn[c[i]] = max(maxn[c[i]],dis[i][j]);
}
}
for(int i = 1; i <= cnt; i++) ans += maxn[i] + 1;
printf("%d",ans);
return 0;
}

浙公网安备 33010602011771号