zoukankan      html  css  js  c++  java
  • 7.12 NOI模拟赛 探险队 期望 博弈 dp 最坏情况下最优策略 可并堆

    LINK:探险队

    avatar
    avatar

    非常难的题目 考试的时候爆零了 完全没有想到到到底怎么做 (当时去刚一道数论题了。

    首先考虑清楚一件事情 就是当前是知道整张地图的样子 但是不清楚到底哪条边断了。

    所以我们要做的其实就是选择最优的路线 使得遇到断边情况下是最优的。

    可以发现在某个点出现断边的时候 此时断的一定是这个点到终点最短路上的边 这样是最差的结果。

    那么其实就是断边只会断由T发出的最短路树上的边。

    到达某个点知道断边之后 其实要求出断开这条边再到T的最短路。

    暴力是(nmcdot logn)的 考虑更快的求法 当前点可以通过非最短路边到达另外一个点 或者是自己子树内的一条边到达另外的点再沿着另外的点的最短路走。

    维护子树内这样边的最小值可以线段树 最快的做法是 可并堆。记(h_x)表示x处断了最短路的边到T的最小值。

    复杂度(ncdot logn).

    接下来考虑怎么求答案.

    一个比较(naiv)e的想法是 不是求最优线路么?可以把所有最优的线路给搜出来。

    对于初始选择这条线路的最大代价为(max{h_x+dis_x})所有之中选(min)即可。

    这启示我们求S到T的所有路径的代价为上述的那个东西 从S开始dp没有什么用 因为当前的选择 需要知道以后的决策。

    如 当前如果选择断边 是要知道以后断边的结果会更好 当前选择不断边 要知道以后断边的结果会更差.

    所以需要倒序dp.

    (f_x)表示从x到T的最短路/设(f_{x,0/1})表示从x到T的最短路此时是否断边了.

    那么其实我们知道以后的状态就可以决断现在的状态了.

    (f_{x,0}=f_{tn,0}+e[i],f_{x,1}=max(min(f_{tn,1}+e[i]),h_x))

    最后的答案即为(f_{S,1}) 由此可以发现好像第一个状态不是很必要。

    为了让状态具有最优性可以利用dij进行转移。

    感觉很难 希望以后遇到这种问题可以会做吧!

    code
    //#include<bitsstdc++.h>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cmath>
    #include<cctype>
    #include<cstdlib>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<vector>
    #include<algorithm>
    #include<utility>
    #include<bitset>
    #include<set>
    #include<map>
    #define ll long long
    #define db double
    #define INF 1000000000000000000ll
    #define ldb long double
    #define pb push_back
    #define put_(x) printf("%d ",x);
    #define get(x) x=read()
    #define gt(x) scanf("%d",&x)
    #define gi(x) scanf("%lf",&x)
    #define put(x) printf("%d
    ",x)
    #define putl(x) printf("%lld
    ",x)
    #define gc(a) scanf("%s",a+1)
    #define rep(p,n,i) for(RE int i=p;i<=n;++i)
    #define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
    #define fep(n,p,i) for(RE int i=n;i>=p;--i)
    #define vep(p,n,i) for(RE int i=p;i<n;++i)
    #define pii pair<ll,int>
    #define mk make_pair
    #define RE register
    #define P 1000000007
    #define gf(x) scanf("%lf",&x)
    #define pf(x) ((x)*(x))
    #define uint unsigned long long
    #define ui unsigned
    #define EPS 1e-4
    #define sq sqrt
    #define S second
    #define F first
    #define V vector
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
        return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
        RE int x=0,f=1;RE char ch=getc();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
        return x*f;
    }
    const int MAXN=200010,maxn=500010<<1;
    int n,m,ss,tt,cnt,len=1;
    ll dis[MAXN],h[MAXN],c[MAXN];
    int vis[MAXN],v[MAXN],f[MAXN][20],Log[MAXN],d[MAXN];V<int>g[MAXN],s[MAXN];
    int lin[MAXN],pos[maxn],nex[maxn],ver[maxn],e[maxn];
    int root[MAXN],lc[maxn],rc[maxn],b[maxn];
    pii w[maxn];
    inline void add(int x,int y,int z)
    {
    	ver[++len]=y;
    	nex[len]=lin[x];
    	lin[x]=len;
    	e[len]=z;
    }
    priority_queue<pii> q;
    inline void dij()
    {
    	q.push(mk(0,tt));
    	rep(1,n,i)dis[i]=INF;dis[tt]=0;
    	while(q.size())
    	{
    		int x=q.top().S;q.pop();
    		if(vis[x])continue;
    		vis[x]=1;
    		go(x)
    		{
    			if(dis[tn]>dis[x]+e[i])
    			{
    				dis[tn]=dis[x]+e[i];
    				f[tn][0]=x;v[tn]=i;
    				q.push(mk(-dis[tn],tn));
    			}
    		}
    	}
    }
    inline void dfs(int x)
    {
    	d[x]=d[f[x][0]]+1;
    	rep(1,Log[d[x]],i)f[x][i]=f[f[x][i-1]][i-1];
    	vep(0,g[x].size(),j)dfs(g[x][j]);
    }
    inline int LCA(int x,int y)
    {
    	if(d[x]<d[y])swap(x,y);
    	fep(Log[d[x]],0,i)if(d[f[x][i]]>=d[y])x=f[x][i];
    	if(x==y)return x;
    	fep(Log[d[x]],0,i)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    inline int merge(int x,int y)//左偏树的性质 左边比右边满.
    {
    	if(!x||!y)return x|y;
    	if(w[x]>w[y])swap(x,y);
    	rc[x]=merge(rc[x],y);
    	if(b[lc[x]]<b[rc[x]])swap(lc[x],rc[x]);
    	b[x]=b[rc[x]]+1;
    	return x;
    }
    inline void dp(int x)
    {
    	vep(0,g[x].size(),i)dp(g[x][i]),root[x]=merge(root[x],root[g[x][i]]);
    	vep(0,s[x].size(),i)root[x]=merge(root[x],s[x][i]);
    	while(root[x])
    	{
    		if(d[w[root[x]].S]>=d[x])
    		{
    			root[x]=merge(lc[root[x]],rc[root[x]]);
    			continue;
    		}
    		else 
    		{
    			h[x]=w[root[x]].F-dis[x];
    			break;
    		}
    	}
    }
    int main()
    {
    	//freopen("1.in","r",stdin);
    	freopen("expedition.in","r",stdin);
        freopen("expedition.out","w",stdout);
    	get(n);get(m);get(ss);get(tt);
    	if(ss==tt){puts("0");return 0;}
    	rep(1,m,i)
    	{
    		int get(x),get(y),get(z);
    		add(x,y,z);add(y,x,z);
    	}
    	dij();Log[0]=-1;
    	rep(1,n,i)
    	{
    		if(f[i][0])g[f[i][0]].pb(i);
    		Log[i]=Log[i>>1]+1;
    		pos[v[i]>>1]=1;h[i]=INF;c[i]=INF;vis[i]=0;
    	}
    	dfs(tt);
    	rep(1,m,i)
    	{
    		if(pos[i])continue;
    		w[++cnt]=mk(dis[ver[i<<1]]+dis[ver[i<<1|1]]+e[i<<1],LCA(ver[i<<1],ver[i<<1|1]));
    		s[ver[i<<1]].pb(cnt);++cnt;w[cnt]=w[cnt-1];s[ver[i<<1|1]].pb(cnt);
    	}
    	dp(tt);
    	q.push(mk(0,tt));c[tt]=0;h[tt]=0;
    	while(q.size())
    	{
    		int x=q.top().S;q.pop();
    		if(vis[x])continue;
    		vis[x]=1;
    		go(x)
    		{
    			if(c[tn]>max(c[x],h[x])+e[i])
    			{
    				c[tn]=max(c[x],h[x])+e[i];
    				q.push(mk(-max(c[tn],h[tn]),tn));
    			}
    		}
    	}
    	if(max(h[ss],c[ss])>=INF)puts("-1");
    	else putl(max(h[ss],c[ss]));
    	return 0;
    }
    
  • 相关阅读:
    HDU 5044 Tree 树链剖分
    HDU 3966 Aragorn's Story 动态树 树链剖分
    HDU 2475 BOX 动态树 Link-Cut Tree
    上阶段总结
    HDU 3487 Play with Chain | Splay
    HDU 3726 Graph and Queries 平衡树+前向星+并查集+离线操作+逆向思维 数据结构大综合题
    VIJOS P1081 野生动物园 SBT、划分树模板
    VIJOS P1647 不差钱 SBT
    HDU 1890 Robotic Sort | Splay
    基础练习(VIP部分-持续更新)
  • 原文地址:https://www.cnblogs.com/chdy/p/13305590.html
Copyright © 2011-2022 走看看