Question Link
题目大意
n个点,每个点都有一个拥挤度,m条边,由 u 到 v 点的代价是 v 的拥挤度 减去 u 的拥挤度 的差 的立方, 由题意分析可知,这是一个有向图,接下来q个数字,num i ,这个数字 numi 代表结点,问由结点 1 到 numi ,的情况。如果不能联通,或者,到达他的代价总和,小于 3 则,输出 ?
题目分析
由于是立方,所以我们知道这个图的边是有负权的,这就意味这,这个图可能有负环,在大量数据面前,绝对有负环回路,dijkstra,根据贪心思想来考虑的算法显然行不通。只有,bellman or SPFA , 类似于暴力枚举的方法。
关键来了
代价总和小于等于 3 如果代价总和为 负数 同样满足条件,那么意味着,能有负环回路到达的点,这个点的代价总和,绝对会是负数,走过负权回路的次数越多,代价越小,所以我们知道,在负权回路中的点,和 负权回路能到达的点,一定输出 ?。 但是,这是充分条件,而非必要条件。有不经过负权回路,但是dis的值是小于 3 的结点。所以 我们判断,在能由负环到达的点,dis 小于 3 的点, 无法到达的点,都输出 ? 。
接下来是一些细节(基于SPFA)
如何判断点能被负环达到?
c结点 入队次数超过 n 那么 , c结点能被负环达到,意味着在经过n个(n-1也行)结点后,还能被松弛,那么他一定能够被负环到达。这时候,如果,d结点这个结点c结点,到达,那么起点 1 到达 d 结点的代价总和,在历经很多次负环后,一定会是负数。
为什么已经判断为负环的点不能进入下次松弛?
因为入队次数 是 无限 , 会炸表。
细节添加
保存入队次数输出数组 intime
是否能被负环到达的数组 necir
AC code
#include<iostream>
#include<queue>
#include<cstring>
#include<string>
#include<sstream>
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<stack>
#include<ctime>
using namespace std;
#define rep(i,aa,bb) for(register int i=aa;i<=bb;i++)
#define rrep(i,aa,bb) for(register int i=aa;i>=bb;i--)
#define mset(var,val) memset(var,val,sizeof(var))
#define LL long long
#define eps 0.000001
#define inf 0x3f3f3f3f
#define llinf 1e18
#define exp 0.000001
#define pai 3.141592654
#define random(x) rand()%(x)
#define lowbit(x) x&(-x)
inline int read()
{
int x=0,y=1;char a=getchar();while ( a>'9' || a<'0'){if ( a=='-')y=-1;a=getchar();}
while ( a>='0' && a<='9' ){ x=(x<<3)+(x<<1)+a-'0'; a=getchar();}return x*y;
}
#define N 220
int cro[N],n,m;bool inq[N],necir[N];int intime[N],dis[N];
struct Ead{
int u,v,w,nx;
}e[N*N];int hea[N],tot;
void init(){
mset(cro,0);
mset(inq,0);
mset(necir,0);
mset(intime,0);
mset(hea,0);
mset(dis,0);
tot = 0;
}
void add_ead(int u,int v,int w){
++tot;
e[tot].u = u; e[tot].v = v; e[tot].w = w; e[tot].nx = hea[u]; hea[u] = tot;
}
void dfs(int nod){
necir[ nod ] = 1;
for (int i = hea[ nod ] ; i ; i = e[i].nx ){
if ( !necir[ e[i].v ] )
dfs( e[i].v );
}
}
void SPFA(int st){
rep(i,1,n) dis[ i ] = inf ;
dis[ st ] = 0;
queue< int >q;
q.push(st);
inq[ st ] = 1;
intime[ st ] = 1;
while ( !q.empty() ){
int u = q.front(); q.pop();
inq[ u ] = 0 ;
if ( necir[ u ] ) continue;
for (int i = hea[ u ] ; i ; i = e[i].nx ){
if ( dis[ e[i].v ] > dis[ u ] + e[ i ].w && e[i].w != inf && dis[u] != inf ){
dis[ e[i].v ] = dis[ u ] + e[ i ].w;
if ( !inq[ e[i].v ] ) {
inq[ e[i].v ] = 1;
intime[ e[i].v ]++;
q.push( e[i].v );
if ( intime[ e[i].v ] > n ){
dfs( e[i].v );
}
}
}
}
}
}
int main()
{
// freopen("1.txt","r",stdin);
// srand((int)time(0));
// std::ios::sync_with_stdio(false);
int T; T = read();
rep(aaai,1,T){
init();
n = read();
rep(i,1,n) cro[i] = read();
m = read();
rep(i,1,m){
int u,v,w;
u = read(); v = read(); w = (cro[v]-cro[u])*(cro[v]-cro[u])*(cro[v]-cro[u]);
add_ead(u,v,w);
}
SPFA(1);
int q = read();
printf("Case %d:\n",aaai);
rep(i,1,q){
int tem = read();
if ( necir[ tem ] || dis[ tem ] < 3 || dis[ tem ] == inf ){
printf("?\n");
}
else printf("%d\n",dis[tem]);
}
}
return 0;
}