zoukankan      html  css  js  c++  java
  • Luogu P3783 [SDOI2017]天才黑客

    题目大意

    一道码量直逼猪国杀图论+数据结构题。我猪国杀也就一百来行

    首先我们要看懂鬼畜的题意,发现其实就是在一个带权有向图上,每条边有一个字符串信息。让你找一个点出发到其它点的最短路径。听起来很简单,手速码完Dijkstra

    然而这题中除了路径上的边权和还要加上去的就是一条路径上所有边代表的字符串的LCP,还有一点就是所有的字符串都是一棵字典树上的路径。

    问题有点复杂啊,那我们慢慢分析。


    化边为点

    考虑到如果我们在跑最短路的时候记录每个点过来的状态就会不可避免的扩出(O(n^2))条边,直接GG

    仔细一想这道题最难的部分在哪?其实还是求LCP部分,所以显然边的重要性比点大,因此我们化边为点

    具体的说,我们把原先的每条边拆成一个入点一个出点,在这两点之间连边表示原来的边权。

    那么现在我们只需要在这些边化为的点直接连接边权为两字符串LCP的边即可(从一个出点连至一个入点)


    构建虚树

    那么现在我们就要找到原来每个点,将所有与其相连边所需要的节点(不管是进入还是出去)之间的边互相连接。

    乍一看这样的点数多的爆炸,但是仔细一想,总点数再怎么多也不会超过(2m)啊!

    所以我们对这些点构造一颗虚树(不会虚树的左转Luogu P2495 [SDOI2011]消耗战

    此时虚树上的点就是它子树内的点(包括他本身)之间的LCA,所以我们只要在这些点对之间连边,边权为这个点的(dep-1)(减不减(1)根据个人写法不同)


    优化建边

    像前面说的那样,直接两两暴力连边边数还是平方级别的。

    我们发现这些点都要经过LCA,所以可以考虑以LCA为中转点建虚点连边。(不会建虚点的左转Luogu P1983 车站分级

    然后每次连边的边数是(O(n))的,但是有由于有多个点,所以总的边数还是(O(n^2))优化了半天却好似放屁

    但是我们发现这个时候我们连的边相当于是从某个子树连到某个点,再从这个点连到某个子树

    子树的一个经典性质,相信我不说大家都知道:DFS序连续。

    所以问题变成区间向点,点向区间连边。这个由于我比较菜(不会前后缀优化建图),同时这题的数据范围不大,因此我们可以用最经典的线段树优化建图(不会的左转Luogu P3588 [POI2015]PUS

    那么我们只需要同时维护一棵出线段树和一棵入线段树即可


    总结&&CODE

    最后问题回归到最短路问题,那么这题就被我们轻松艰难的切掉了。

    考虑复杂度,由于有线段树的存在总边数是(O(nlog n))的(认为(n,m)同阶)

    算上Dijkstra的一个(log)复杂度为(O(nlog^2n))

    千万注意码的时候要注意自己在干什么,不要不小心把自己码晕了。

    附上200+的CODE

    // luogu-judger-enable-o2
    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    #define RI register int
    #define pb push_back
    #define Ms(f,x) memset(f,x,sizeof(f))
    #define Tp template <typename T>
    using namespace std;
    typedef long long LL;
    const int N=50005,K=20005;
    int t,n,m,k,dep[K],dfn[K],rv[2][K],tot;
    class FileInputOutput
    {
    	private:
    		#define S 1<<21
    		#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
    		#define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
    		char Fin[S],Fout[S],*A,*B; int Ftop,pt[25];
    	public:
    		Tp inline void read(T &x)
    		{
    			x=0; char ch; while (!isdigit(ch=tc()));
    			while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
    		}
    		Tp inline void write(T x)
    		{
    			if (!x) return (void)(pc('0'),pc('
    ')); RI ptop=0;
    			while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('
    ');
    		}
    		inline void Fend(void)
    		{
    			fwrite(Fout,1,Ftop,stdout);
    		}
    		#undef S
    		#undef tc
    		#undef pc
    }F;
    inline bool cmp(int x,int y)
    {
    	return dfn[x]<dfn[y];
    }
    class LCA_Solver
    {
    	private:
    		#define P 15
    		struct edge
    		{
    			int to,nxt;
    		}e[K]; int head[K],cnt,idx,anc[K][P],x,y,z;
    		inline void add(int x,int y)
    		{
    			e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
    		}
    		inline void reset(int now)
    		{
    			for (RI i=0;i<P-1;++i) anc[now][i+1]=anc[anc[now][i]][i];
    		}
    		#define to e[i].to
    		inline void DFS(int now,int fa)
    		{
    			dep[now]=dep[fa]+1; reset(now); dfn[now]=++idx;
    			for (RI i=head[now];i;i=e[i].nxt) anc[to][0]=now,DFS(to,now);
    		}
    		#undef to
    		inline void swap(int &x,int &y)
    		{
    			int t=x; x=y; y=t;
    		}
    	public:
    		inline void init(void)
    		{
    			cnt=idx=0; Ms(head,0); for (RI i=1;i<k;++i)
    			F.read(x),F.read(y),F.read(z),add(x,y); DFS(1,0);
    		}
    		inline int query(int x,int y)
    		{
    			RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i)
    			if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x;
    			for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
    			x=anc[x][i],y=anc[y][i]; return anc[x][0];
    		}
    }L;
    class Graph_Solver
    {
    	private:
    		#define NS 1000005
    		#define MS 4000005
    		struct edge
    		{
    			int to,nxt,v;
    		}e[MS]; int head[NS],cnt,pos[NS],st,a,b,d,pcnt,p[N<<1]; bool vis[NS]; LL dis[NS],c;
    		int num[2][K<<2],stack[K],top,father[K],size[K],ndfn[K];;
    		struct data
    		{
    			LL val; int id;
    			inline data(LL Val=0,int Id=0) { val=Val; id=Id; }
    			inline friend bool operator <(data A,data B)
    			{
    				return A.val>B.val;
    			}
    		}; priority_queue <data> small; vector <int> v[N][2];
    		inline void add(int x,int y,int z)
    		{
    			e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
    		}
    		inline void build(int opt,int now,int l,int r)
    		{
    			num[opt][now]=++tot; if (l==r) return (void)(rv[opt][p[l]]=tot);
    			int mid=l+r>>1; build(opt,now<<1,l,mid); build(opt,now<<1|1,mid+1,r);
    			if (!opt) add(num[opt][now<<1],num[opt][now],0),add(num[opt][now<<1|1],num[opt][now],0);
    			else add(num[opt][now],num[opt][now<<1],0),add(num[opt][now],num[opt][now<<1|1],0);
    		}
    		#define O beg,end,id,val
    		inline void link(int opt,int now,int l,int r,int beg,int end,int id,int val)
    		{
    			if (l>=beg&&r<=end) { if (!opt) add(num[opt][now],id,val); else add(id,num[opt][now],val); return; }
    			int mid=l+r>>1; if (beg<=mid) link(opt,now<<1,l,mid,O); if (end>mid) link(opt,now<<1|1,mid+1,r,O);
    		}
    		#undef O
    		inline void VT_build(void)
    		{
    			RI i; father[stack[top=1]=p[1]]=0;
    			int t=pcnt; for (i=2;i<=t;++i)
    			{
    				int fa=L.query(stack[top],p[i]);
    				while (top&&dep[stack[top]]>dep[fa])
    				{
    					if (top==1||dep[stack[top-1]]<=dep[fa])
    					father[stack[top]]=fa; --top;
    				}
    				if (fa!=stack[top]) father[fa]=stack[top],stack[++top]=fa,p[++pcnt]=fa;
    				stack[++top]=p[i]; father[p[i]]=fa;
    			}
    			sort(p+1,p+pcnt+1,cmp); for (i=1;i<=pcnt;++i) size[p[i]]=1;
    			for (i=pcnt;i;--i) size[father[p[i]]]+=size[p[i]],ndfn[p[i]]=i;
    			for (build(0,1,1,pcnt),build(1,1,1,pcnt),i=1;i<=pcnt;++i)
    			{
    				int now=p[i]; ++tot; link(0,1,1,pcnt,i,i,tot,dep[now]-1);
    				link(1,1,1,pcnt,i,i+size[now]-1,tot,0);	int fa=father[now]; if (!fa) continue;
    				++tot; link(0,1,1,pcnt,i,i+size[now]-1,tot,dep[fa]-1);
    				if (ndfn[fa]<=i-1) link(1,1,1,pcnt,ndfn[fa],i-1,tot,0);
    				if (i+size[now]<=ndfn[fa]+size[fa]-1) link(1,1,1,pcnt,i+size[now],ndfn[fa]+size[fa]-1,tot,0);
    			}
    		}
    		Tp inline void miner(T &x,T y)
    		{
    			if (y<x) x=y;
    		}
    	public:
    		inline void init(void)
    		{
    			RI i; for (i=1;i<=n;++i) v[i][0].clear(),v[i][1].clear();
    			for (cnt=tot=0,Ms(head,0),i=1;i<=m;++i)
    			{
    				F.read(a); F.read(b); F.read(c); F.read(d);
    				pos[tot+1]=d; pos[tot+2]=d; v[a][1].pb(tot+1); v[b][0].pb(tot+2);
    				add(tot+1,tot+2,c); tot+=2;
    			}
    			pos[st=++tot]=1; v[1][0].pb(st);
    		}
    		inline void build(void)
    		{
    			RI j,k; int lim; for (RI i=1;i<=n;++i)
    			{
    				for (pcnt=k=0;k<2;++k)
    				for (lim=v[i][k].size(),j=0;j<lim;++j)
    				p[++pcnt]=pos[v[i][k][j]];
    				sort(p+1,p+pcnt+1); pcnt=unique(p+1,p+pcnt+1)-p-1;
    				sort(p+1,p+pcnt+1,cmp); VT_build();
    				for (k=0;k<2;++k) for (lim=v[i][k].size(),j=0;j<lim;++j)
    				{
    					int t=v[i][k][j]; if (!k) add(t,rv[k][pos[t]],0); else add(rv[k][pos[t]],t,0);
    				}
    			}
    		}
    		#define to e[i].to
    		inline void Dijkstra(void)
    		{
    			Ms(dis,63); Ms(vis,0); small.push(data(dis[st]=0,st));
    			while (!small.empty())
    			{
    				int now=small.top().id; small.pop();
    				if (vis[now]) continue; vis[now]=1;
    				for (RI i=head[now];i;i=e[i].nxt)
    				if (dis[to]>dis[now]+e[i].v)
    				small.push(data(dis[to]=dis[now]+e[i].v,to));
    			}
    		}
    		#undef to
    		inline void print(void)
    		{
    			for (RI i=2;i<=n;++i)
    			{
    				int lim=v[i][0].size(); LL ans=1e18;
    				for (RI j=0;j<lim;++j) miner(ans,dis[v[i][0][j]]);
    				F.write(ans);
    			}
    		}
    }G;
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	for (F.read(t);t;--t)
    	{
    		F.read(n); F.read(m); F.read(k); G.init();
    		L.init(); G.build(); G.Dijkstra(); G.print();
    	}
    	return F.Fend(),0;
    }
    
  • 相关阅读:
    jQuery遍历div,判断是否为空,为空时执行某个操作
    ps中扩展画布的时候,不能选择扩展画布部分的颜色解决方法
    PS中缩放工具的细微缩放不可以使用的解决方法
    Excel的基础操作
    Word文档编辑
    人脸识别活体检测之张张嘴和眨眨眼——login.jsp
    人脸识别活体检测之张张嘴和眨眨眼——web.xml
    人脸识别活体检测之张张嘴和眨眨眼——readme
    maven用途、核心概念、用法、常用参数和命令、扩展
    [转载]ORACLE临时表的创建
  • 原文地址:https://www.cnblogs.com/cjjsb/p/10202170.html
Copyright © 2011-2022 走看看