test1
P7293 Sum of Distances P
首先注意一个点不能不动,那么假如一个点 \((a_1, a_2 \dots a_k) \to (b_1, b_2 \dots b_k)\),相当于 \(a_1 \to b_1, a_2 \to b_2 \dots a_k \to b_k\) 这 \(k\) 个点分别走。那么如果走最短路,快的就可以通过走某一条邻边并反复横跳等待慢的。但是假如有两个点奇偶性不同,就必须走一个奇环来更改最短路的奇偶性。那么相当于要求 \(\forall i, dis_i = dis (a_i \to b_i)\) 的奇偶性都相同。
那么跑出每个点 \(i\) 到 \(1\) 的奇偶最短路 \(dis_{i, 0 /1}\),那么对于一个选择方案 \((a_1, a_2 \dots a_k)\),则需要的最短步数是 \(\min({\max_{i = 1}^n dis_{i, 0}}, \max_{i = 1}^n dis_{i, 1})\)。可以直接 \(\rm DP\) 做到 \(O(n^3) / O(n^4)\)。
考虑这个转化:\(\min(a, b) = a + b - \max(a, b)\),若 \(a, b\) 是形如 \(\max_{i = 1} ^n x_i\) 的柿子,那么这个东西就可以转化为 \(3\) 个子问题。于是设 \(f_{i, j}\) 为考虑前 \(i\) 个图,\(\max = j\) 的方案数,然后按 \(m_i\) 排序后的顺序 \(\rm DP\) 可以保证第二维的和为 \(O(n)\),前缀和优化就可以 \(O(n)\),使用滚动数组可以做到空间复杂度 \(O(n)\)。于是 \(O(n \log n) / O(n)\) 即可完成。
代码没有排序,但是过了。
qwq
#include<bits/stdc++.h>
#define ll long long
#define pb emplace_back
#define pir pair<int, ll>
#define fi first
#define se second
#define inv(x) qpow(x, mod - 2)
#define il inline
#define mkpir make_pair
using namespace std;
const int N = 4e5 + 10, M = 2e5 + 10;
const ll mod = 1e9 + 7;
il ll qpow(ll& x, ll y){
ll ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
il void chkmin(ll& x, ll y){if(y < x) x = y;}
il void chkmin(int& x, int y){if(y < x) x = y;}
il void chkmax(int& x, int y){if(y > x) x = y;}
il void chkmax(ll& x, ll y){if(y > x) x = y;}
il void ADD(ll& x, ll y){x += y; (x >= mod) ? (x -= mod) : 0;}
il void MUL(ll& x, ll y){x = x * y % mod;}
int k, st[N], dis[N][2], n, mx[N], len[N];
struct edge{
int v, next;
}edges[N << 1];
int head[N], idx;
void add_edge(int u, int v){
edges[++idx] = {v, head[u]};
head[u] = idx;
}
void bfs(int s){
queue<pir> Q; dis[s][0] = 0; Q.push(mkpir(s, 0));
while(!Q.empty()){
int u = Q.front().first, op = Q.front().second; Q.pop();
for(int i = head[u]; i; i = edges[i].next){
int v = edges[i].v;
if(dis[v][op ^ 1] > dis[u][op] + 1) dis[v][op ^ 1] = dis[u][op] + 1, Q.push(mkpir(v, op ^ 1));
}
}
}
struct DP{
int lim;
ll f[N], g[N], s[N], dif[N];
void init(){f[0] = 1;}
void cpy(int V){
//cerr << V << "\n";
for(int i = 0; i <= lim; i++) ADD(dif[i], i ? dif[i - 1] : 0ll), MUL(g[i], dif[i]), ADD(f[i], g[i]);//, cerr << i << " " << dif[i] << " " << g[i] << " " << f[i] << "\n";
s[0] = g[0] = f[0]; f[0] = 0; chkmax(lim, V);
for(int i = 1; i <= lim; i++) dif[i] = 0, s[i] = (s[i - 1] + f[i]) % mod, g[i] = f[i], f[i] = 0;//, cerr << i << " " << dif[i] << " " << g[i] << " " << f[i] << "\n";
}
void add(int x){
if(x > lim) return;
ADD(dif[x + 1], 1); ADD(f[x], s[x]);
}
ll getans(){
cpy(lim); ll ans = 0;
for(int i = 1; i <= lim; i++) ADD(ans, i * g[i] % mod);//, cerr << g[i] << "\n";
//cerr << lim << " " << ans << "\n";
return ans;
}
}d1, d2, d3;
// for y: f_{max(x, y)} += g_x
// f_y += s_y
// x > y : f_x += g_x
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> k; st[0] = 1; memset(dis, 0x3f, sizeof dis); int INF = dis[0][0];
for(int G = 1; G <= k; G++){
//cerr << G << "\n";
int m; cin >> len[G] >> m; st[G] = st[G - 1] + len[G - 1]; n += len[G];
for(int i = 1; i <= m; i++){
int x, y; cin >> x >> y; x += st[G] - 1; y += st[G] - 1;
add_edge(x, y); add_edge(y, x);
} bfs(st[G]);
for(int i = st[G]; i < st[G] + len[G]; i++){
if(dis[i][0] < INF) chkmax(mx[G], dis[i][0]);
if(dis[i][1] < INF) chkmax(mx[G], dis[i][1]);
//cerr << dis[i][0] << " " << dis[i][1] << "\n";
}
} d1.init(); d2.init(); d3.init();
for(int G = 1; G <= k; G++){
d1.cpy(mx[G]); d2.cpy(mx[G]); d3.cpy(mx[G]);
for(int i = st[G]; i < st[G] + len[G]; i++) d1.add(dis[i][0]), d2.add(dis[i][1]), d3.add(max(dis[i][1], dis[i][0]));
} cout << ((d1.getans() + d2.getans() - d3.getans()) % mod + mod) % mod << "\n";
return 0;
}