题目
题目描述
小奇所在的国家一共由
n
n
n个城市和
m
m
m条连接这些城市的双向道路组成。
小奇非常喜欢骑自行车,它常常骑着自行车从一个城市,沿着某些双向道路到达另一个城市。
现在,这个国家要关闭其所有的道路以便翻修,但为了保证必要的交通运输,第 i i i条道路会在第 i i i天暂时开放。
小奇为了了解本次翻修对它旅行的影响,因此想知道,如果它第 L \mathcal{L} L天在一个城市 s s s,在第 R \mathcal{R} R天或之前是否能到达城市 t t t。当然,小奇不需要第 L \mathcal{L} L天就立即离开 ,也不需要恰好在第 R \mathcal{R} R天到达城市 。
为了更全面地评估这个影响,小奇会有许多的询问,但它一下子算不过来,就只好找你帮忙了。
输入格式
第一行三个正整数
n
,
m
,
q
n,m,q
n,m,q,分别表示城市数、道路数、询问数。
接下来有 m m m行,其中第 i + 1 i+1 i+1行有两个正整数 u i , v i ( u i ≠ v i ) u_i,v_i(u_i\ne v_i) ui,vi(ui=vi),表示有一条连接 u i , v i u_i,v_i ui,vi两座城市的双向道路,并且这条道路在第 i i i天暂时开放。
接下来 q q q行,每行四个正整数 l i , r i , s i , t i l_i,r_i,s_i,t_i li,ri,si,ti,表示一个询问。
输出格式
q
q
q行,第
i
i
i行表示第
i
i
i个询问的答案,可行输出
Yes
\text{Yes}
Yes,不可行输出
No
\text{No}
No。
数据范围与约定
对于
30
%
30\%
30%的数据,
m
,
q
≤
2000
m,q\le 2000
m,q≤2000;
对于所有数据均有: 2 ≤ n ≤ 1000 , 1 ≤ m , q ≤ 200000 , 1 ≤ l i ≤ r i ≤ m , 1 ≤ s i , t i ≤ n , s i ≠ t i 2\le n\le 1000,1\le m,q\le 200000,1\le l_i\le r_i\le m,1\le s_i,t_i\le n,s_i\ne t_i 2≤n≤1000,1≤m,q≤200000,1≤li≤ri≤m,1≤si,ti≤n,si=ti。
不保证整张图连通,不保证有且仅有一条道路连接两座城市。
思路
直接大力 D P \Bbb{DP} DP。
用 f i ( x , y ) f_i(x,y) fi(x,y)表示第 i i i天在 x x x,最早什么时候到 y y y。对第 i i i天的行为进行决策,直接可以写出转移方程:(用 ( u , v ) (u,v) (u,v)表示边权)
f i ( x , y ) = min [ f i + 1 ( x , y ) , min ( x , z ) = i f i + 1 ( z , y ) ] f_{i}(x,y)=\min[f_{i+1}(x,y),\min_{(x,z)=i}f_{i+1}(z,y)] fi(x,y)=min[fi+1(x,y),(x,z)=iminfi+1(z,y)]
好像有 n 2 m n^2m n2m个状态?太多了吧!
仔细想想, f i + 1 ( ? , ? ) f_{i+1}(?,?) fi+1(?,?)和 f i ( ? , ? ) f_{i}(?,?) fi(?,?)的区别在哪里?只在于第 i i i条边的端点!只有第 i i i条边的端点不是从 f i + 1 ( ? , ? ) f_{i+1}(?,?) fi+1(?,?)直接拷贝过来的。
有点像长链剖分那种优化,使用 滚动数组。只需要从大到小枚举每一条边,进行状态转移即可。
等等!似乎会导致同层转移。也就是说, f i ( x , y ) f_i(x,y) fi(x,y)会从 f i ( z , y ) f_i(z,y) fi(z,y)转移过来。
但是没关系!因为上面这种转移也是对的(你走过去再走回来没人拦着你)。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
inline int readint(){
int a = 0, f = 1; char c = getchar();
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -1;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxN = 1000;
int n, m, q;
struct Query{
int L, R, S, T, id;
void input(){
L = readint(), R = readint();
S = readint(), T = readint();
}
bool operator<(const Query &that)const{
return L < that.L;
}
}query[200001];
struct Edge{
int one, two;
void input(){
one = readint(), two = readint();
}
};
Edge ppl[200001];
void init(){
n = readint(), m = readint(), q = readint();
for(int i=1; i<=m; ++i)
ppl[i].input();
for(int i=1; i<=q; ++i){
query[i].input();
query[i].id = i;
}
sort(query+1,query+q+1);
}
int dp[MaxN+1][MaxN+1];
bool answers[200001];
void solve(){
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
dp[i][j] = (1<<20);
int nowq = q;
for(int i=m,x,y; i; --i){
x = ppl[i].one, y = ppl[i].two;
dp[x][y] = min(dp[x][y],i);
dp[y][x] = min(dp[y][x],i);
for(int j=1; j<=n; ++j){
if(j != x) dp[y][j] = min(dp[y][j],dp[x][j]);
if(j != y) dp[x][j] = min(dp[x][j],dp[y][j]);
}
while(nowq and query[nowq].L == i){
answers[query[nowq].id] = (dp[query[nowq].S][query[nowq].T] <= query[nowq].R);
-- nowq;
}
}
for(int i=1; i<=q; ++i){
if(answers[i]){
putchar('Y');
putchar('e');
putchar('s');
}
else{
putchar('N');
putchar('o');
}
putchar('\n');
}
}
int main(){
// freopen("travel.in","r",stdin);
// freopen("travel.out","w",stdout);
init();
solve();
return 0;
}