zoukankan      html  css  js  c++  java
  • [TJOI2012]桥

    Description
    有n个岛屿,m座桥,每座桥连通两座岛屿,桥上会有一些敌人,玩家只有消灭了桥上的敌人才能通过,与此同时桥上的敌人会对玩家造成一定伤害。而且会有一个大Boss镇守一座桥,以玩家目前的能力,是不可能通过的。而Boss是邪恶的,Boss会镇守某一座使得玩家受到最多的伤害才能从岛屿1到达岛屿n(当然玩家会选择伤害最小的路径)。
    问,Boss可能镇守桥有哪些。

    Input
    第一行两个整数n,m
    接下来m行,每行三个整数s,t,c,表示一座连接岛屿s和t的桥上的敌人会对玩家造成c的伤害。
    1<=n<=100000,1<=m<=200000,1<=c<=10000,数据保证玩家可以从岛屿1到达岛屿n

    Output
    一行,两个整数d,cnt,d表示有Boss的情况下,玩家要受到的伤害,cnt表示Boss有可能镇守的桥的数目。

    Sample Input
    3 4
    1 2 1
    1 2 2
    2 3 1
    2 3 1

    Sample Output
    3 1


    首先简化题面,问去掉原图的一条边之后,令新图S到T的最短路长度最大的方案数

    我们要明白一件事,割的边肯定是最短路径上的边,那么我们就找一条最短路径,考虑经过其他的边的贡献。如果经过其他路径的话,我们一定是走形如(S ightarrow A ightarrow B ightarrow T)的形式,并且(A ightarrow B)一定是一条边,是路径我就在其中找一条边出来充当(A ightarrow B)

    那么(A ightarrow B)会对哪些点造成贡献呢?首先明白(S ightarrow A)的路线,一定是(S ightarrow x ightarrow A),其中x是最短路径上的某一个点,那么有贡献的肯定是x到T路径上的点。上界找完了,下界呢?

    既然上界是根据S开始走的路线,那么我们反着求一遍最短路,找一个类似的点y满足(T ightarrow y ightarrow B),那么下界肯定就是y了。

    也就是说这条路径形如(S ightarrow x ightarrow A ightarrow B ightarrow y ightarrow T),因此说明只要(x ightarrow y)之间的某条路径被删了,我们就可以走(A ightarrow B)这条路线,这样我们就可以把走(A ightarrow B)的贡献加到区间(x ightarrow y)中,维护最小值,最后取出所有路径中的最大值统计个数即可

    这样弄好之后,我们相当于枚举一条边,在一段区间进行更新,这样我们就可以用线段树来进行维护了

    /*program from Wolfycz*/
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define inf 0x7f7f7f7f
    using namespace std;
    typedef long long ll;
    typedef unsigned int ui;
    typedef unsigned long long ull;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')    f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<1)+(x<<3)+ch-'0';
    	return x*f;
    }
    inline void print(int x){
    	if (x>=10)	print(x/10);
    	putchar(x%10+'0');
    }
    const int N=1e5,M=2e5;
    int pre[(M<<1)+10],now[N+10],child[(M<<1)+10],val[(M<<1)+10];
    int dfn[N+10],L[N+10],R[N+10],h[N+10];
    bool vis[N+10],In_way[N+10],flag[(M<<1)+10];
    int n,m,tot,num,Ans,Max,Dis;
    struct S1{
    	#define ls (p<<1)
    	#define rs (p<<1|1)
    	#define fa (p>>1)
    	struct node{
    		int x,v;
    		bool operator <(const node &a)const{return v<a.v;}
    	}Q[N+10];
    	int tot;
    	void insert(int x,int v){
    		Q[++tot]=(node){x,v};
    		int p=tot;
    		while (p!=1&&Q[p]<Q[fa])	swap(Q[p],Q[fa]),p=fa;
    	}
    	void Delete(){
    		Q[1]=Q[tot--];
    		int p=1,son;
    		while (ls<=tot){
    			if (rs>tot||Q[ls]<Q[rs])	son=ls;
    			else	son=rs;
    			if (Q[son]<Q[p])	swap(Q[p],Q[son]),p=son;
    			else	break;
    		}
    	}
    }Heap;
    struct Segment{
    	int tree[(N<<2)+10];
    	Segment(){memset(tree,127,sizeof(tree));}
    	void Add_tag(int p,int v){tree[p]=min(tree[p],v);}
    	void pushdown(int p){
    		if (tree[p]==inf)	return;
    		Add_tag(ls,tree[p]);
    		Add_tag(rs,tree[p]);
    	}
    	void Modify(int p,int l,int r,int x,int y,int v){
    		if (x<=l&&r<=y){
    			Add_tag(p,v);
    			return;
    		}
    		int mid=(l+r)>>1;
    		if (x<=mid)	Modify(ls,l,mid,x,y,v);
    		if (y>mid)	Modify(rs,mid+1,r,x,y,v);
    	}
    	void traversals(int p,int l,int r){
    		if (l==r){
    			if (tree[p]>Max)	Max=tree[p],Ans=0;
    			Ans+=tree[p]==Max;
    			return;
    		}
    		pushdown(p);
    		int mid=(l+r)>>1;
    		traversals(ls,l,mid);
    		traversals(rs,mid+1,r);
    	}
    }Tree;
    struct S2{
    	int dis[N+10];
    	S2(){memset(dis,63,sizeof(dis));}
    }Frw,Bck;
    void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;}
    void insert(int x,int y,int z){join(x,y,z),join(y,x,z);}
    void dijkstra(int x,int *dis){
    	memset(vis,0,sizeof(vis));
    	Heap.insert(x,dis[x]=0);
    	while (Heap.tot){
    		int Now=Heap.Q[1].x;
    		Heap.Delete();
    		if (vis[Now])	continue;
    		vis[Now]=1;
    		for (int p=now[Now],son=child[p];p;p=pre[p],son=child[p]){
    			if (dis[son]>dis[Now]+val[p]){
    				dis[son]=dis[Now]+val[p];
    				Heap.insert(son,dis[son]);
    			}
    		}
    	}
    }
    void Find_way(int x){
    	In_way[x]=1;
    	dfn[x]=num++;
    	for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
    		if (Frw.dis[x]+val[p]+Bck.dis[son]==Dis){
    			flag[p]=flag[p+(p&1?1:-1)]=1;
    			Find_way(son);
    			return;
    		}
    	}
    }
    void bfs(S2 T,int *A){
    	int head=1,tail=0;
    	for (int i=1;i<=n;i++)	if (In_way[i])	A[h[++tail]=i]=dfn[i];
    	for (;head<=tail;head++){
    		int Now=h[head];
    		for (int p=now[Now],son=child[p];p;p=pre[p],son=child[p]){
    			if (!In_way[son]&&!A[son]&&T.dis[Now]+val[p]==T.dis[son]){
    				h[++tail]=son;
    				A[son]=A[Now];
    			}
    		}
    	}
    }
    int main(){
    	n=read(),m=read();
    	for (int i=1;i<=m;i++){
    		int x=read(),y=read(),z=read();
    		insert(x,y,z);
    	}
    	dijkstra(1,Frw.dis);
    	dijkstra(n,Bck.dis);
    	Dis=Frw.dis[n];
    	Find_way(1),num--;
    	bfs(Frw,L),bfs(Bck,R);
    	for (int x=1;x<=n;x++)
    		for (int p=now[x],son=child[p];p;p=pre[p],son=child[p])
    			if (!flag[p]&&~L[x]&&~R[son]&&L[x]<R[son])
    				Tree.Modify(1,1,num,L[x]+1,R[son],Frw.dis[x]+val[p]+Bck.dis[son]);
    	Tree.traversals(1,1,num);
    	printf("%d %d
    ",Max,Max==Dis?m:Ans);
    	return 0;
    }
    
  • 相关阅读:
    Solr Admin管理界面使用说明
    游戏设计手札07
    php中新浪微博的调用
    关于MFC中EDIT编辑框内容换行显示
    几个常用命令
    ANF
    关于MFC中的定时器用法
    C++ 堆方面的笔记
    关于类定义与IO操作
    mysql用户权限配置时遇到的问题
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/9745288.html
Copyright © 2011-2022 走看看