【LOJ】#2270. 「SDOI2017」天才黑客
题解
显然要记录每个点来的状态,这样会扩充出点度的平方条边,就gg了
删掉所有的点,把每个边拆成两个点,连一条边权为c
这个时候我们考虑对于原先的每个点,将所有与其相连边所需要的节点(不管是进入还是出去)建一棵虚树,然后用线段树优化建图,优化方法是枚举每个lca,然后将lca的每个子树和其他子树连一条长度为lca深度的边,也就是dfs序上连续的一段
进到这个点的边拆出来的点和负责出去的线段树的子节点连一条边
负责进来的线段树向这个点出去的节点连一条边
跑最短路就行
然后就做完了……
写的我真累= =
我非常好奇到底如何能只写3.xK????
update:貌似有更清奇的写法不用线段树orzzzzz
代码
(7.6K的sd代码,不看也罢)
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
//#define ivorysi
#define MAXN 200005
#define MAXK 80005
typedef long long int64;
using namespace std;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int N,M,K;
priority_queue<pair<int64,int> > Q;
namespace Tree {
struct node {
int next,to;
}E[MAXK * 2];
int head[MAXK],sumE,dfn[MAXK],idx,dep[MAXK];
int st[MAXK * 2][17],len[MAXK * 2],pos[MAXK],tot;
void add(int u,int v) {
E[++sumE].next = head[u];
E[sumE].to = v;
head[u] = sumE;
}
int min_dep(int a,int b) {
return dep[a] < dep[b] ? a : b;
}
int lca(int a,int b) {
a = pos[a];b = pos[b];
if(a > b) swap(a,b);
int l = len[b - a + 1];
return min_dep(st[a][l],st[b - (1 << l) + 1][l]);
}
void dfs(int u,int fa) {
dfn[u] = ++idx;
st[++tot][0] = u;pos[u] = tot;dep[u] = dep[fa] + 1;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(v != fa) {
dfs(v,u);
st[++tot][0] = u;
}
}
}
void Init() {
sumE = 0;memset(head,0,sizeof(head));idx = 0;
memset(st,0,sizeof(st));
int u,v,w;
for(int i = 1 ; i < K ; ++i) {
read(u);read(v);read(w);
add(u,v);
}
idx = 0;tot = 0;
dfs(1,0);
for(int i = 2 ; i <= tot ; ++i) len[i] = len[i / 2] + 1;
for(int j = 1 ; j <= 16 ; ++j) {
for(int i = 1 ; i <= tot ; ++i) {
st[i][j] = min_dep(st[i][j - 1],st[i + (1 << j - 1)][j - 1]);
}
}
}
}
namespace Graph {
struct node {
int next,to;
int64 val;
}E[4000005];
int head[1000005],sumE,pos[1000005],Ncnt,S;
vector<int> vec[MAXN][2];
int Line[200005],tot,sta[MAXK],top,faAux[MAXK],siz[MAXK],d[MAXK],pz[2][MAXK],tr[2][MAXK * 8];
int64 dis[1000005];
bool vis[1000005];
bool cmp(int a,int b) {
return Tree::dfn[a] < Tree::dfn[b];
}
void add(int u,int v,int64 c) {
E[++sumE].to = v;
E[sumE].next = head[u];
E[sumE].val = c;
head[u] = sumE;
}
void Init() {
sumE = 0;Ncnt = 0;memset(head,0,sizeof(head));
for(int i = 1 ; i <= N ; ++i) {vec[i][0].clear();vec[i][1].clear();}
int a,b,d;
int64 c;
for(int i = 1 ; i <= M ; ++i) {
read(a);read(b);read(c);read(d);
pos[Ncnt + 1] = d;pos[Ncnt + 2] = d;
vec[a][1].pb(Ncnt + 1);vec[b][0].pb(Ncnt + 2);
add(Ncnt + 1,Ncnt + 2,c);
Ncnt += 2;
}
S = ++Ncnt;
pos[S] = 1;vec[1][0].pb(S);
}
void Build_SegmentTree(int id,int u,int L,int R) {
tr[id][u] = ++Ncnt;
if(L == R) {pz[id][Line[R]] = Ncnt;return;}
int mid = (L + R) >> 1;
Build_SegmentTree(id,u << 1,L,mid);
Build_SegmentTree(id,u << 1 | 1,mid + 1,R);
if(!id) {add(tr[id][u << 1],tr[id][u],0);add(tr[id][u << 1 | 1],tr[id][u],0);}
else {add(tr[id][u],tr[id][u << 1],0);add(tr[id][u],tr[id][u << 1 | 1],0);}
}
void Add_Edge(int id,int u,int L,int R,int l,int r,int v,int64 c) {
if(L == l && R == r) {
if(!id) {add(tr[id][u],v,c);}
else {add(v,tr[id][u],c);}
return;
}
int mid = (L + R) >> 1;
if(r <= mid) Add_Edge(id,u << 1,L,mid,l,r,v,c);
else if(l > mid) Add_Edge(id,u << 1 | 1,mid + 1,R,l,r,v,c);
else {Add_Edge(id,u << 1,L,mid,l,mid,v,c);Add_Edge(id,u << 1 | 1,mid + 1,R,mid + 1,r,v,c);}
}
void Build_AuxTree() {
top = 0;
sta[++top] = Line[1];faAux[Line[1]] = 0;
int c = tot;
for(int i = 2 ; i <= c ; ++i) {
int f = Tree::lca(sta[top],Line[i]);
while(top >= 1 && Tree::dep[sta[top]] > Tree::dep[f]) {
if(top == 1 || Tree::dep[sta[top - 1]] <= Tree::dep[f]) {
faAux[sta[top]] = f;
}
--top;
}
if(f != sta[top]) {
faAux[f] = sta[top];
sta[++top] = f;
Line[++tot] = f;
}
sta[++top] = Line[i];
faAux[Line[i]] = f;
}
sort(Line + 1,Line + tot + 1,cmp);
for(int i = 1 ; i <= tot ; ++i) siz[Line[i]] = 1;
for(int i = tot ; i >= 1 ; --i) {
siz[faAux[Line[i]]] += siz[Line[i]];
d[Line[i]] = i;
}
Build_SegmentTree(0,1,1,tot);
Build_SegmentTree(1,1,1,tot);
for(int i = 1 ; i <= tot ; ++i) {
int u = Line[i];
++Ncnt;
Add_Edge(0,1,1,tot,i,i,Ncnt,Tree::dep[u] - 1);
Add_Edge(1,1,1,tot,i,i + siz[u] - 1,Ncnt,0);
int f = faAux[u];
if(f) {
++Ncnt;
Add_Edge(0,1,1,tot,i,i + siz[u] - 1,Ncnt,Tree::dep[f] - 1);
if(d[f] <= i - 1)
Add_Edge(1,1,1,tot,d[f],i - 1,Ncnt,0);
if(d[f] + siz[f] - 1 >= i + siz[u])
Add_Edge(1,1,1,tot,i + siz[u],d[f] + siz[f] - 1,Ncnt,0);
}
}
}
void Build_Graph() {
for(int i = 1 ; i <= N ; ++i) {
tot = 0;
for(int k = 0 ; k <= 1 ; ++k) {
for(int j = 0 ; j < vec[i][k].size() ; ++j) {
Line[++tot] = pos[vec[i][k][j]];
}
}
sort(Line + 1,Line + tot + 1);
tot = unique(Line + 1,Line + tot + 1) - Line - 1;
sort(Line + 1,Line + tot + 1,cmp);
Build_AuxTree();
for(int k = 0 ; k <= 1 ; ++k) {
for(int j = 0 ; j < vec[i][k].size() ; ++j) {
int t = vec[i][k][j];
if(!k) add(t,pz[k][pos[t]],0);
else add(pz[k][pos[t]],t,0);
}
}
}
}
void Dijkstra() {
for(int i = 1 ; i <= Ncnt ; ++i) {
dis[i] = 1e16;vis[i] = 0;
}
dis[S] = 0;
Q.push(mp(-dis[S],S));
while(!Q.empty()) {
pair<int64,int> now = Q.top();Q.pop();
int u = now.se;
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u] ; i; i = E[i].next) {
int v = E[i].to;
if(dis[v] > dis[u] + E[i].val) {
dis[v] = dis[u] + E[i].val;
Q.push(mp(-dis[v],v));
}
}
}
}
void Print() {
for(int i = 2 ; i <= N ; ++i) {
int64 ans = 1e16;
for(int j = 0 ; j < vec[i][0].size() ; ++j) ans = min(ans,dis[vec[i][0][j]]);
out(ans);enter;
}
}
}
void Solve() {
read(N);read(M);read(K);
Graph::Init();
Tree::Init();
Graph::Build_Graph();
Graph::Dijkstra();
Graph::Print();
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
int T;
read(T);
while(T--) Solve();
return 0;
}
md我zz吧
我代码是别人的两倍,运行时间也是别人的两倍啊
我附加点为什么最后都建到1000000去了,不应该啊,然后还0.5s?是LOJ的评测机扩充了我的想象力吗
好写???
没觉得好写啊???
我觉得刷新了我难写的上限啊???
思路和细节写的比NOI2018D2T2还累啊,我代码能力难道真的退化了???