题面
X X X 国有 N N N 个城市(编号 1 ∼ N 1\sim N 1∼N),由 M M M 条单向道路连接,其中 S S S 城是 X X X 国的首都。
每个城市 i i i 有一个发达指数 a i a_i ai ,我们定义城市 i i i 的经济状况为首都 S S S 到城市 i i i 任意一条路径上两个不同的城市 x , y x,y x,y 的 a x m o d a y a_x \mod a_y axmoday 的最大值。( x x x 和 y y y 必须在同一条路径上, x , y x,y x,y 可以是 i i i 或者 S S S )
有 Q Q Q 次询问,每次询问一个城市的经济状况
注意:一个点可以被经过多次!
N , Q ≤ 4 × 1 0 5 , M ≤ 2 × 1 0 6 , 1 ≤ a i ≤ 1 0 9 N,Q \leq 4 \times 10^5,M \leq 2 \times 10^6,1 \leq a_i \leq 10^9 N,Q≤4×105,M≤2×106,1≤ai≤109
思路
首先我们考虑如果是在一个序列
a
a
a 上,求
a
x
m
o
d
a
y
a_x \mod a_y
axmoday 的最大值
对于每一对有序对
x
,
y
x,y
x,y ,设
a
x
m
o
d
a
y
=
m
x
a_x \mod a_y = mx
axmoday=mx
- 如果 a x > x y a_x > x_y ax>xy , m x < a y mx < a_y mx<ay
- 如果 a x = x y a_x = x_y ax=xy , m x = 0 mx = 0 mx=0
- 如果 a x < x y a_x < x_y ax<xy , m x = a x mx = a_x mx=ax
因此可以得到 a x m o d a y ≤ min ( a x , a y ) a_x \mod a_y \leq \min(a_x,a_y) axmoday≤min(ax,ay),那么对于每一组 ( a x , a y ) (a_x,a_y) (ax,ay),如果两者不相等,则答案可以取到较小值,那么全局的答案也可以很容易得到:序列中的严格次小值。
我们考虑在这道题中的情况。首先访问的点次数不限,因此一个强连通分量中的点全都可以访问,那么我们缩点以后令每个强连通分量的权值为一个二元组,为这个强连通分量中的最大值和次大值(如果次大值没有置为空即可)。
↑
\uparrow
↑ 以上都是好想的
现在我们考虑缩完点之后的 topo 怎么做
本来想了个伪做法,被指出挂了,现在放在这里自省(其他地方不重要,看 topo 就行)
#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define int long long
#define rpt(i,a,n) for(int i = a;i <= n;++i)
#define swap(x,y) (x ^= y ^= x ^= y)
#define debug cout<<"Help!\n"
constexpr int N = 4e5 + 5;
int dfn[N],low[N],stac[N],bel[N],mx1[2][N],mx2[2][N],ans[N],a[N],ru[N],n,m,q,s,u,v,cnt,top,idx;
bool vis[N];
vector <int> g[N],t[N];
void tarjan(int u){
dfn[u] = low[u] = ++cnt;
stac[++top] = u;
vis[u] = true;
for(auto v : g[u]){
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(vis[v]) low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u]){
int cur;
++idx;
do {
cur = stac[top--];
vis[cur] = false;
bel[cur] = idx;
if(mx1[0][idx] < a[cur]) mx2[0][idx] = mx1[0][idx],mx1[0][idx] = a[cur];
else if(mx1[0][idx] ^ a[cur]) mx2[0][idx] = max(mx2[0][idx],a[cur]);
} while(cur ^ u);
}
}
queue <int> Q;
template <typename T>
inline void read(T & x){
x = 0;
char c;
for(c = getchar();!isdigit(c);c = getchar());
for(;isdigit(c);c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
}
template <typename T>
inline void write(T x){
if(x > 9) write(x / 10);
putchar((x % 10 + '0'));
}
signed main(){
read(n),read(m),read(q),read(s);
rep(i,n) read(a[i]);
while(m--){
read(u),read(v);
g[u].emplace_back(v);
}
tarjan(s);
rep(i,n) if(bel[i]) for(auto j : g[i]) if(bel[j] && bel[j] ^ bel[i]) t[bel[i]].emplace_back(bel[j]),++ru[bel[j]];
rep(i,idx) ans[i] = mx2[0][i],mx1[1][i] = mx1[0][i],mx2[1][i] = mx2[0][i];
Q.push(bel[s]);
while(Q.size()){
u = Q.front(),Q.pop();
for(auto v : t[u]){
int tmp[4] = {mx1[1][u],mx2[1][u],mx1[0][v],mx2[0][v]};
sort(tmp,tmp + 4);
if(tmp[2] ^ tmp[3]) m = tmp[2];
else if(tmp[1] ^ tmp[3]) m = tmp[1];
else m = tmp[0];
ans[v] = max(ans[v],m),mx2[1][v] = m;
if(tmp[3] > mx1[1][v]) mx2[1][v] = mx1[1][v],mx1[1][v] = tmp[3];
else if(tmp[3] ^ mx1[1][v]) mx2[1][v] = max(mx2[1][v],tmp[3]);
else mx2[1][v] = max(mx2[1][v],m);
if(!--ru[v]) Q.push(v);
}
}
while(q--){
read(u);
if(!bel[u]) putchar('-'),putchar('1'),putchar(' ');
else write(ans[bel[u]]),putchar(' ');
}
return 0;
}
挂哪了?挂在
m
x
1
和
m
x
2
mx1 和 mx2
mx1和mx2 需要在同一条路径上
于是不会做了呜呜呜
A thousand years later…
学长说:做两遍 topo ,第一遍统计路径上的严格次大值在强连通分量
v
v
v 里面时
a
n
s
v
ans_v
ansv 的最大值,第二遍用
S
S
S 到
v
v
v 路径上的强连通分量里面的答案去更新
a
n
s
v
ans_v
ansv
嗯,然后就做完了
代码
#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define int long long
constexpr int N = 4e5 + 5;
int dfn[N],low[N],stac[N],bel[N],mx1[N],mx2[N],ans[N],a[N],ru[N],tmp[N],mx[N],n,m,q,s,u,v,cnt,top,idx;
bool vis[N];
vector <int> g[N],t[N];
void tarjan(int u){
dfn[u] = low[u] = ++cnt;
stac[++top] = u;
vis[u] = true;
for(auto v : g[u]){
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(vis[v]) low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u]){
int cur;
++idx;
do {
cur = stac[top--];
vis[cur] = false;
bel[cur] = idx;
if(mx1[idx] < a[cur]) mx2[idx] = mx1[idx],mx1[idx] = a[cur];
else if(mx1[idx] ^ a[cur]) mx2[idx] = max(mx2[idx],a[cur]);
} while(cur ^ u);
}
}
queue <int> Q;
template <typename T>
inline void read(T & x){
x = 0;
char c;
for(c = getchar();!isdigit(c);c = getchar());
for(;isdigit(c);c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
}
template <typename T>
inline void write(T x){
if(x > 9) write(x / 10);
putchar((x % 10 + '0'));
}
signed main(){
read(n),read(m),read(q),read(s);
rep(i,n) read(a[i]);
while(m--){
read(u),read(v);
g[u].emplace_back(v);
}
tarjan(s);
rep(i,n) if(bel[i]) for(auto j : g[i]) if(bel[j] && bel[j] ^ bel[i]) t[bel[i]].emplace_back(bel[j]),++ru[bel[j]];
rep(i,idx) ans[i] = mx2[i],mx[i] = mx1[i],tmp[i] = ru[i];
Q.push(bel[s]);
while(Q.size()){
u = Q.front(),Q.pop();
for(auto v : t[u]){
if(mx[u] ^ mx1[v]) ans[v] = max(ans[v],min(mx1[v],mx[u]));
mx[v] = max(mx[v],mx[u]);
if(!--ru[v]) Q.push(v);
}
}
rep(i,idx) ru[i] = tmp[i];
Q.push(bel[s]);
while(Q.size()){
u = Q.front(),Q.pop();
for(auto v : t[u]){
ans[v] = max(ans[v],ans[u]);
if(!--ru[v]) Q.push(v);
}
}
while(q--){
read(u);
if(!bel[u]) putchar('-'),putchar('1'),putchar(' ');
else write(ans[bel[u]]),putchar(' ');
}
return 0;
}
467

被折叠的 条评论
为什么被折叠?



