zoukankan      html  css  js  c++  java
  • 在ZBQC随机游走

    前言

    这道题总思考时间不到 (1) 分钟,获得了 (0pt) 的好成绩!

    原因竟是我在 T3 试图用 (O(n)) 的时间解决二维偏序问题!

    题目

    ( m 4s 256MB.)

    ( t PPL) 在做不出来题的时候喜欢随机游走。

    可以将 ( m ZBQC) 看做有 (n) 个点的有向完全图,每个点有一个权值 (a_u),表示 (u ightarrow v) 的边权。

    但是 ( m ZBQC) 并不是一成不变的,它有时候也会翻新,当然次数很少,具体的,它接下来有 (m) 翻新操作,每次操作会更改一条边的边权。

    这天 ( t PPL) 在随机游走的时候想到一个问题,如果他把所有点对 ((i,j)) 之间的最短路都走一遍,总路程是多少?

    显然 ( t PPL) 不会算,只能交给你了。

    (1le nle 10^5;1le mle 3000;0le a_ile 10^6.)

    讲解

    从数据范围入手,我们发现 (m) 很小,和 (n) 不是一个数量级,思考有什么用。

    这里大概有个经典 ( t Trick),在这种完全图中,存在一些特殊的边/点(比如这道题就是存在一些改了的边),一般来说这样构成的连通块会比较小,把这些特殊的边/点构成的连通块处理一下,再处理一下块与块之间的信息,题就解决了。

    这玩意好像之前碰到过一次,不然我为什么觉得是经典 Trick。

    回到这道题,考虑把每个弱连通块内部的最短路处理出来,显然连通块总大小不会超过 (m)。这时我们可以枚举起点,再用线段树优化 ( t dijkstra) 来得到块内的最短路,注意块内的最短路不一定只在块内走,有可能出块再入块,这种情况下一定是出去到一个最小的 (a) 再回来,然后我们就得到了块内的贡献。

    出块的贡献就很简单了,只需要在块内找一个最优的点出去即可。

    时间复杂度 (O(n+m^2log_2m))

    据说有个坑点:一条边可能会被修改多次,要取最后那次。不知道数据有没有这样的情况,反正我判了一下。

    代码

    不卡常!
    //12252024832524
    #include <bits/stdc++.h>
    #define TT template<typename T>
    using namespace std;
    
    typedef long long LL;
    const int MAXN = 100005;
    const int INF = 0x3f3f3f3f;
    int n,m;
    int a[MAXN],suf[MAXN];
    LL ans;
    
    LL Read()
    {
    	LL x = 0,f = 1; char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    struct edge
    {
    	int v,w;
    	bool operator < (const edge &px)const{
    		return v < px.v;
    	}
    };
    set<edge> G[MAXN]; 
    
    int f[MAXN];
    int findSet(int x){if(f[x]^x)f[x]=findSet(f[x]);return f[x];}
    
    vector<int> p[MAXN];
    
    #define lc (x<<1)
    #define rc (x<<1|1)
    int MIN[MAXN<<2],ID[MAXN<<2],lz[MAXN<<2];
    void calc(int x,int val){if(MIN[x]^(INF+1))MIN[x] = Min(MIN[x],val),lz[x] = Min(lz[x],val);}
    void up(int x)
    {
    	if(MIN[lc] <= MIN[rc]) MIN[x] = MIN[lc],ID[x] = ID[lc];
    	else MIN[x] = MIN[rc],ID[x] = ID[rc];
    }
    void down(int x)
    {
    	if(lz[x] == INF) return;
    	calc(lc,lz[x]); calc(rc,lz[x]);
    	lz[x] = INF;
    }
    void Build(int x,int l,int r,int now)
    {
    	lz[x] = INF;
    	if(l == r) {if(l^now)MIN[x] = INF;else MIN[x] = 0;ID[x] = l;return;}
    	int mid = (l+r) >> 1;
    	Build(lc,l,mid,now); Build(rc,mid+1,r,now);
    	up(x);
    }
    void Change(int x,int l,int r,int ql,int qr,int val)
    {
    	if(ql > qr) return;
    	if(ql <= l && r <= qr) {calc(x,val);return;}
    	int mid = (l+r) >> 1;
    	down(x);
    	if(ql <= mid) Change(lc,l,mid,ql,qr,val);
    	if(mid+1 <= qr) Change(rc,mid+1,r,ql,qr,val);
    	up(x);
    }
    void fk(int x,int l,int r,int pos)
    {
    	if(l == r){MIN[x] = INF+1;return;}//INF+1 : unchangeable
    	int mid = (l+r) >> 1;
    	down(x);
    	if(pos <= mid) fk(lc,l,mid,pos);
    	else fk(rc,mid+1,r,pos);
    	up(x);
    }
    int dis[MAXN],to[MAXN];
    
    int main()
    {
    //	freopen("happybean.in","r",stdin);
    //	freopen("happybean.out","w",stdout);
    	n = Read(); m = Read();
    	for(int i = 1;i <= n;++ i) a[i] = Read(),f[i] = i;
    	for(int i = 1;i <= m;++ i)
    	{
    		int u = Read(),v = Read(),w = Read();
    		int U = findSet(u),V = findSet(v);
    		if(U > V) f[U] = V;
    		else f[V] = U;
    		auto it = G[u].lower_bound(edge{v,0});
    		if(it->v == v) G[u].erase(it);
    		G[u].insert(edge{v,w});
    	}
    	for(int i = 1;i <= n;++ i) p[findSet(i)].emplace_back(i);
    	suf[n+1] = INF;
    	for(int i = n;i >= 1;-- i)
    	{
    		suf[i] = suf[i+1];
    		for(auto A : p[i]) suf[i] = Min(suf[i],a[A]);
    	}
    	for(int i = 1,nowmin = INF;i <= n;++ i)
    	{
    		if(!p[i].size()) continue;
    		for(int j = 0,N = p[i].size()-1;j <= N;++ j) to[p[i][j]] = j;
    		for(int j = 0,N = p[i].size()-1;j <= N;++ j)//begining
    		{
    			Build(1,0,N,j);
    			for(int k = 0;k <= N;++ k)//dijkstra : pop queue
    			{
    				int now = ID[1],lst = 0,x = p[i][now]; dis[now] = MIN[1];
    				for(auto A : G[p[i][now]])
    				{
    					Change(1,0,N,lst,to[A.v]-1,dis[now]+a[x]);
    					Change(1,0,N,to[A.v],to[A.v],dis[now]+A.w);
    					lst = to[A.v]+1;
    				}
    				if(lst <= N) Change(1,0,N,lst,N,dis[now]+a[x]);
    				fk(1,0,N,now);
    			}
    			int wdnmd = a[p[i][j]]+Min(nowmin,suf[i+1]);
    			for(int k = 0;k <= N;++ k) dis[k] = Min(dis[k],wdnmd);
    			int v = INF;
    			for(int k = 0;k <= N;++ k) ans += dis[k],v = Min(v,dis[k]+a[p[i][k]]);//inside
    			ans += v * (n-N-1ll);//outside
    		}
    		for(auto A : p[i]) nowmin = Min(nowmin,a[A]);
    	}
    	Put(ans,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    趣题:寻找出现了奇数次的数
    zstu2016校赛圣杯战争
    HDU 5183 Negative and Positive (NP) ——(后缀和+手写hash表)
    HDU 5673 Robot ——(卡特兰数)
    HDU 3775 Chain Code ——(Pick定理)
    2016 ICPC北京站现场赛总结(再度流水账)
    2014苏州大学新生赛第二场(12.10)题目解析
    【Jump Game II 】cpp
    【Jump Game】cpp
    【 Sqrt(x) 】cpp
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15535698.html
Copyright © 2011-2022 走看看