【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还累啊,我代码能力难道真的退化了???

posted @ 2018-10-03 19:51  sigongzi  阅读(369)  评论(0编辑  收藏  举报