zoukankan      html  css  js  c++  java
  • 2019-2020 XX Open Cup, Grand Prix of Korea

    2019-2020 XX Open Cup, Grand Prix of Korea

    比赛收获

    本场贡献:et3_tsy :G提供了思路,过了H,H爆了int,贡献WA一发,J有想法,调了1hr后,发现在特例情况复杂度退化,假了

    1427314831a:过了A

    Ryker0923 :过了G,提供了A的思路

    一共三道题

    总结:本场暴露两个问题:

    1)后期乏力

    前两个小时过了三题,接下来,FIJ都可以做,但是都没有比较好的想法,说明题量不够,还需要时间沉淀。

    2)特例导致复杂度退化

    算法在实现之前,一定要和队友多交流,多尝试几种特例,一方面看算法错没错,一方面还要看复杂度会不会退化。像et3_tsy 的J题,在处理的时候,看似是树dp,但是随着层数的增多,常数会膨胀,退化成N方,假了。

    部分题解

    A. 模拟

    简单模拟即可,注意很多细节上的问题,以及不要重复计数

    #include<bits/stdc++.h>
    using namespace std;
    int a[1000][1000],b[1000][1000],n,m; 
    int cmp(int x,int y)
    {
    	if(x==6&&y==6)return 1;
    	if(x==6&&y==9)return 1;
    	if(x==7&&y==7)return 1;
    	if(x==8&&y==8)return 1;
    	if(x==9&&y==9)return 1;
    	if(x==9&&y==6)return 1;
    	return 0;
    }
    int main()
    {
    	cin>>n>>m;
    	char c=getchar();
    	for(int i=1;i<=n;i++)
    	{
    	for(int j=1;j<=m;j++)
    	{
    	    a[i][j]=getchar()-'0';
    	}
    	getchar();
        }
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++)
    	{
    		b[n-i+1][m-j+1]=a[i][j];
    	}
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++)
    	{
    		if(b[i][j]==6)b[i][j]=9;
    		else if(b[i][j]==9)b[i][j]=6;
    	}
    	int bj=0;
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++)
    	{
    		if(!cmp(a[i][j],b[i][j]))
    		{
    			bj=1;break;
    		}
    	}
    	if(!bj)
    	{
    		if(n%2==1&&m%2==1&&a[n/2+1][m/2+1]!=8)
            bj=1;
    	}
    	if(bj){
    		cout<<-1;return 0;
    	}
    	int ans=0;
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++)
    	{
    		if(a[i][j]!=b[i][j])ans++;
    		else if(a[i][j]==7)ans++;
    	}
    	cout<<ans/2;
    	return 0;
    }
    

    G. 图论

    注意这道题是让我们从可行解里面选取最小字典序的路径,那么换言之,我们就要优先dfs去查询哪个点是能到达目标点的。

    值得注意的是,因为在求向图可达性中,可能形成强连通,所以直接进行两次连续的dfs,防止漏掉点没有被标记的可达点

    最后是进行对可行点形成的单向路径上进行贪心模拟的过程。至于什么时候输出too long?只要访问了已经访问过的点,就说明它在已有的一个强连通分量里面出不来了,故输出too long即可。

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,s,t,cnt,tot;
    int l[200010],rd[2000010];
    struct node{
    	int x,y,z;
    }ed[600010];
    bool cmp(node a,node b)
    {
    	return a.z>b.z;
    }
    struct tedge{
    	int v,next,w;
    	bool u;
    }e[1000010];
    bool b[200010],used[200010];
    void add(int x,int y,int z)
    {
    	e[++cnt].w=y;
    	e[cnt].v=z;
    	e[cnt].next=l[x];
    	l[x]=cnt;
    }
    bool dfs(int x)
    {
    	if(used[x])
    	return b[x];
    	used[x]=1;
    	for(int i=l[x];i;i=e[i].next)
    	{
    //		cout<<x<<" "<<e[i].w<<endl;
    		if(b[x]==1)
    		dfs(e[i].w);
    		else
    		b[x]=dfs(e[i].w);
    	}
    	return b[x];
    }
    void print()
    {
    	for(int i=1;i<=tot;i++)
    	printf("%d ",rd[i]);
    }
    void dfs2(int x)
    {
    	used[x]=1;
    	for(int i=l[x];i;i=e[i].next)
    	if(b[e[i].w])
    	{
    		rd[++tot]=e[i].v;
    		if(tot>1000000)
    		{
    			printf("TOO LONG");
    			exit(0);
    		}
    		if(e[i].w==t)
    		{
    			print();
    			exit(0);
    		}
    		else if(used[e[i].w])
    		{
    			printf("TOO LONG");
    			exit(0);
    		}
    		else
    		dfs2(e[i].w);
    	}
    }
    int main()
    {
    	cin>>n>>m>>s>>t;
    	int x,y,z;
    	b[t]=1;
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&ed[i].x,&ed[i].y,&ed[i].z);
    	}
    	sort(ed+1,ed+m+1,cmp);
    	for(int i=1;i<=m;i++)
    	{
    //		cout<<ed[i].x<<" "<<ed[i].y<<" "<<ed[i].z<<endl;
    		add(ed[i].x,ed[i].y,ed[i].z);
    	}
    	dfs(s);
    	for(int i=1;i<=n;i++)used[i]=0;
    //	for(int i=1;i<=n;i++)cout<<b[i]<<endl;
    	dfs(s);
    	if(!b[s])
    	{
    		printf("IMPOSSIBLE");
    		return 0;
    	}
    //	for(int i=1;i<=n;i++)cout<<b[i]<<endl;
    	for(int i=1;i<=n;i++)used[i]=0;
    	dfs2(s);
    }
    /*
    3 3 1 3
    1 2 1
    2 1 2
    1 3 3
    */
    

    H. 思维

    给定两个全排列,全排列A可动,B不能动,求最小交换次数,使得Ai-Bi的绝对值之和最大。

    应该先从偶数上思考,即什么时候最优,即把 1到 N/2 分为第一组,把 N/2+1 到 N分为第二组

    那么我们只要保证对于任意一对Ai和Bi恰好一个属于第一组,一个属于第二组即可,显然这样最优。

    而奇数大同小异,唯有不同的是最中间的那个数,它不能分到第一组和第二组中去。

    接下来就是双指针贪心扫描了,分别第二组元素为第一组、第二组的情况,取min即可,注意会爆int

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 300000
    #define ll long long
    #define INF 0x3f3f3f3f3f3f3f3fLL
    int a[maxn],b[maxn];
    ll minn(ll x,ll y)
    {
    	return x<y?x:y;
    }
    int n;
    double mid;
    bool l(int val)
    {
    	return val<mid;
    }
    bool r(int val)
    {
    	return val>mid;
    }
    int main()
    {
    	cin>>n;
    	mid=(0.0+n)/2+0.5;
    	for(int i=1;i<=n;i++)cin>>a[i];
    	for(int i=1;i<=n;i++)cin>>b[i];
    	ll ans=INF;
    	ll cur=0;
    	int cnt=0;
    	int apos=0,bpos=0;
    	cur=0;
    	while(cnt!=n/2)
    	{
    		while(!l(b[++bpos]));
    		while(!r(a[++apos]));
    		cur+=abs(apos-bpos);
    		cnt++;
    	}
    	ans=minn(ans,cur);
    	cur=0;cnt=0;apos=0,bpos=0;
    	while(cnt!=n/2)
    	{
    		while(!r(b[++bpos]));
    		while(!l(a[++apos]));
    		cur+=abs(apos-bpos);
    		cnt++;
    	}
    	ans=minn(ans,cur);
    	cout<<ans<<"
    ";
    	return 0;
    }
    

    J.启发式合并

    大致题意经过转化之后的

    给定n个区段【l, r】, 以及对应的权值,注意他们不交叉

    现在就是求 k (从1->n分别求) 层,至多叠k层的最大权值和为多少

    思路

    显然,我们可以发现他们不交叉只有两种情况,一种是完全包含,一种是完全不相交

    这就有一个树的特征了,建树先排个序,左端第一维(越左越优),区段长第二维(越长越优),再线性扫一遍。

    我们把树建好后,很显然可以在树上去算dp,但是会被下图的特例卡退化成(O(n^2))

    那么我们应该怎么想这道题嘞?

    其实有个很核心的性质

    如果我们在第k层进行贪心选择的时候,k+1层也一定会选上这些

    这一点一定要理解好

    那么我们理解了这个之后,只要利用堆,去存储单层最优的区段贡献即可(对于兄弟之间而言,他们不会产生影响,所以取出两边最优的相加,就是父亲的最优)

    而这一过程可以利用启发式合并的思想进行优化处理

    对于本题, et3_tsy 有一定的收获:

    对于启发式合并而言,并不一定要依赖于树剖,它只是一种思想,就是把小的往大的上面去合并。在进行树dp的时候,一定优先考虑退化问题(比如这题可能会退化成如下情况)。

    https://s1.ax1x.com/2020/11/06/Bf3PgK.png

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 252000
    #define ll long long
    int n,tot;
    ll tmp[maxn];
    int ma[maxn],in[maxn],fa[maxn];
    priority_queue<ll>heap[maxn];
    inline int read()
    {
        int ans=0;
        char last=' ',ch=getchar();
        while(ch<'0'|ch>'9')last=ch,ch=getchar();
        while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
        if(last=='-')ans=-ans;
        return ans;
    }
    void merge_(int x)
    {
        int f=fa[x],fh=ma[f],gh=ma[x];
        if(heap[fh].size()<heap[gh].size())
        {
            ma[f]=gh;
            swap(fh,gh);
        }
        int cnt=0;
        while(!heap[gh].empty())
        {
            tmp[cnt++]=heap[gh].top()+heap[fh].top();
            heap[gh].pop(),heap[fh].pop();
        }
        for(int i=0;i<cnt;i++)heap[fh].push(tmp[i]);
    }
    struct ed
    {
    	int l,r;
    	ll val;
    	bool operator<(const ed& sec)const
    	{
    		if(l!=sec.l)return l<sec.l;
    		return r>sec.r;
    	}
    }e[maxn];
    inline bool check(int l,int r,int curl,int curr)
    {
    	return l<=curl&&r>=curr;
    }
    int main()
    {
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	{
    		e[i].l=read(),e[i].r=read(),e[i].val=read();
    		e[i].r--;
    	}
    	sort(e+1,e+n+1);
    	int curindex=0;
    	for(int i=1;i<=n;i++)
    	{
    		while(curindex&&!check(e[curindex].l,e[curindex].r,e[i].l,e[i].r))
    		{
    			curindex=fa[curindex];
    		}
    		fa[i]=curindex;
    		curindex=i;
    	}
    	queue<int>node;
    	for(int i=1;i<=n;i++)
        {
            ma[i]=i;
            in[fa[i]]++;
        }
        for(int i=1;i<=n;i++)
            if(in[i]==0)node.push(i);
        while(!node.empty())
        {
            int x=node.front();
            node.pop();
            if(x==0)break;
            heap[ma[x]].push(e[x].val);
            merge_(x);
            in[fa[x]]--;
            if(in[fa[x]]==0)node.push(fa[x]);
        }
    	ll ans=0;
    	for(int i=1;i<=n;i++)
    	{
    		if(!heap[ma[0]].empty())
    		{
    		    ans+=heap[ma[0]].top();
    		    heap[ma[0]].pop();
    		}
    		cout<<ans<<" ";
    	}
    
    	return 0;
    }
    
    

    F. Hilbert's Hotel 线段树

    题意:有一家旅馆(有多个房间,序号从0开始)和多个旅行团,现给定三种操作:

    1 k 
    表示新来一个旅行团有k个人.
    当k>0时所有旅馆中已有的人往后移k个房间;
    当k=0时表示来了数不清的人,此时所有旅馆中已有的人移到自己房间标号2倍的房间,再将新加入的人安排在房间号为奇数的房间中。
    
    2 g x
    访问第g个旅行团的第x小的人在那个房间,结果对le9+7取模(g<=当前已有的旅行团个数,1<=x<=第g个旅行团的人数)
    
    3 x
    访问当前第x个房间的人来自那个旅行团(x<=1e9)
    

    题解:

    对于第二种询问,我们可以考虑对每一个旅行团的人所在位置用一个函数(y=kx+b)维护

    当k>0时将所有的已有的bi加上k,再新加入一组(k=1,b=1e9+6)

    当k=0时将所有已有的ki和bi乘2,再新加入一组(k=2,b=1e9+6)

    开两颗线段树分别去维护k和b即可。

    对于第三种询问,我们考率从x这个位置向前回溯已有的操作,以确定x来自那个旅行团

    如若当前k>0时,如果k>x则x一定来自当前操作所在的旅行团,否则令x-=k,回溯上一步操作

    若当前k=0,如果x为奇数,则x来自当前旅行团,否则令x=x/2,回溯上一步操作

    对于k>0的回溯访问,暴力维护复杂度为n,考虑维护所有连续的k>0的和,在此区间上进行二分答案,即可优化为logn

    对于k=0的回溯访问,当x>0时每次至少减少一半,复杂度为logn,但若一开始时x=0,则需跳过这个连续的k=0的区间,否则会进行多次无意义的除二的操作。

    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int p=1e9+7;
    struct tnode{
    	long long l,r,num,plz,mlz;
    }t[1200040][2];
    long long x,y,z,bj,a[300010],binary[300010],caozuo[300100],l1[300010],l2[300010],b[300010];
    inline void build(int g,int i,int l,int r)
    {
    	t[i][g].l=l;t[i][g].r=r;
    	if(l==r){
    //		t[i][g].num=a[l]%p;
    		return;
    	}
    	int mid=(l+r)>>1;
        build(g,i<<1,l,mid);
    	build(g,(i<<1)+1,mid+1,r);	
    	t[i][g].num=(t[i<<1][g].num+t[(i<<1)+1][g].num)%p;
    }
    inline void pushdown(int g,int i)
    {
    	long long k1=t[i][g].plz,k2=t[i][g].mlz;
    	t[i<<1][g].num=(t[i<<1][g].num*k2+k1*(t[i<<1][g].r-t[i<<1][g].l+1))%p;
    	t[(i<<1)+1][g].num=(t[(i<<1)+1][g].num*k2+k1*(t[(i<<1)+1][g].r-t[(i<<1)+1][g].l+1))%p;
    	t[i<<1][g].plz=(t[i<<1][g].plz*k2+k1)%p;
    	t[(i<<1)+1][g].plz=(t[(i<<1)+1][g].plz*k2+k1)%p;
    	t[i<<1][g].mlz=t[i<<1][g].mlz*k2%p;
    	t[(i<<1)+1][g].mlz=t[(i<<1)+1][g].mlz*k2%p;
    	t[i][g].plz=0;
    	t[i][g].mlz=1;
    	return;
    }
    inline void add(int g,int i,int l,int r,long long k)
    {
    	if(t[i][g].l>r||t[i][g].r<l)return;
    	if(l<=t[i][g].l&&r>=t[i][g].r)
    	{
    		t[i][g].num=(t[i][g].num+k*(t[i][g].r-t[i][g].l+1))%p;
    		t[i][g].plz=(t[i][g].plz+k)%p;
    		return;
    	}
    	pushdown(g,i);
    	if(t[i<<1][g].r>=l)add(g,i<<1,l,r,k);
    	if(t[(i<<1)+1][g].l<=r)add(g,(i<<1)+1,l,r,k);
    	t[i][g].num=(t[i<<1][g].num+t[(i<<1)+1][g].num)%p;
    	return;
    }
    inline void mul(int g,int i,int l,int r,long long k)
    {
    	if(t[i][g].l>r||t[i][g].r<l)return;
    	if(l<=t[i][g].l&&t[i][g].r<=r)
    	{
    		t[i][g].num=t[i][g].num*k%p;
    		t[i][g].plz=t[i][g].plz*k%p;
    		t[i][g].mlz=t[i][g].mlz*k%p;
    		return;
    	}
    	pushdown(g,i);
    	if(t[i<<1][g].r>=l)mul(g,i<<1,l,r,k);
    	if(t[(i<<1)+1][g].l<=r)mul(g,(i<<1)+1,l,r,k);
    	t[i][g].num=(t[i<<1][g].num+t[(i<<1)+1][g].num)%p;
    	return;
    }
    inline long long sum(int g,int i,int l,int r)
    {
    	if(t[i][g].l>r||t[i][g].r<l)return 0;
    	if(t[i][g].l>=l&&t[i][g].r<=r)
    	{
    		return t[i][g].num%p;
    	}
    	pushdown(g,i);
    	long long res=0;
    	if(t[i<<1][g].r>=l)res=(res+sum(g,i<<1,l,r))%p;
    	if(t[(i<<1)+1][g].l<=r)res=(res+sum(g,(i<<1)+1,l,r))%p;
    	return res%p;
    }
    int main()
    {
    //freopen("std.in","r",stdin);
    	//freopen("std.out","w",stdout);
    	int m;
    	cin>>m; 
    	build(0,1,0,m);
    	build(1,1,0,m);
    	for(int i=1;i<=4*m;i++)
    	{
    	t[i][0].mlz=1;
    	t[i][1].mlz=1;
        }
    //	cout<<sum(1,2,n)<<endl;
        int k,tot=0;
        add(0,1,0,0,1);
        add(1,1,0,0,p-1);
    	while(m--)
    	{
    		scanf("%d",&bj);
    		if(bj==1)
    		{
    		    tot++;
    		    scanf("%d",&k);
    		    if(k)
    		    {	    	 
    		    	a[tot]=k;
    		    	if(caozuo[tot-1]==1)l1[tot]=l1[tot-1];
    		    	else l1[tot]=tot;
    		        caozuo[tot]=1;
    				add(1,1,0,tot-1,k);
    				add(1,1,tot,tot,p-1);
    				add(0,1,tot,tot,1);
    			}
    			else
    			{
    				caozuo[tot]=2;
    				if(caozuo[tot-1]==2)l2[tot]=l2[tot-1];
    				else l2[tot]=tot;
    				mul(0,1,0,tot-1,2);
    				mul(1,1,0,tot-1,2);
    				add(0,1,tot,tot,2);
    				add(1,1,tot,tot,p-1);
    			}
    			a[tot]+=a[tot-1];
    		}
    		if(bj==2)
    		{
    		    int g,x;
    		    scanf("%d%d",&g,&x);
    		    k=sum(0,1,g,g);
    		    int b=sum(1,1,g,g);
    		    printf("%lld
    ",(1ll*k*x+b)%p);
    		}
    		if(bj==3)
    		{
    			int x;
    			scanf("%d",&x);
    			int cnt=tot;
    			while(cnt>0)
    			{
    				if(caozuo[cnt]==1)
    				{
    				     int l=l1[cnt],r=cnt;
    				     if(a[r]-a[l-1]<=x)
    				     {
    				     	x-=a[r]-a[l-1];
    				     	cnt=l-1;
    					 }
    					 else
    					 {
    					 	int mid=(l+r)>>1;
    					 	while(l<r-1)
    					 	{
    					 		mid=(l+r)>>1;
    					 		if(a[cnt]-a[mid-1]>x)l=mid;
    					 		else r=mid;
    						}
    						if(x<a[cnt]-a[r-1])
    						{
    							cnt=r;break;
    						}
    						else
    						{
    							cnt=l;break;
    						}
    					 }
    				}
    				else
    				{
    					if(x&1)break;
    					if(x==0)
    					{
    						cnt=max(l2[cnt]-1,0ll);
    						break;
    					}
    					else
    					{
    						x>>=1;
    						cnt--;
    					}
    				}
    			}
    			printf("%d
    ",cnt);
    		}
    	}
    	return 0; 
    }
    

    I.最小直径生成树

    这道是一道板子题

    求最小直径生成树,在oi-wiki上有相关内容

    先用Floyd或者Johnson跑出全员最短路

    再对每个点排序一遍去确定自远及近的点是谁

    下面来确定绝对中心

    绝对中心可能是边,可能是点

    对于点来讲,它只要选择最远的两个点作为最远的点,其他点都比他小,相加就是他们的直径。跑一遍迪杰斯特拉即可

    对于边来说,我们找到绝对中心对应的边之后,对于它两个边上的点u,v初始化不应该都是0,否则就会出现下面的错误,。假定我们的绝对中心在4这条蓝色边上,它两端初始化的dis均是0,对于箭头指向的点,选绿3,或者紫1,在dij中均是等价的,但是我们很容易发现,在直径的求解中,如果选紫1显然它的值会更小(因为它的产生的代价已经由黑3承担了)

    https://s3.ax1x.com/2020/11/14/DChnQe.png

    为了处理这个问题,最小直径生成树提出了一个思路

    它的绝对中心一开始在中间,谁的最远距离远就离谁近

    如果记绝对中心距离u点的距离为(disu),由u点支配的最远的点距离u的距离为(faru)

    绝对中心距离v点的距离为(disv),由v点支配的最远的点距离v的距离为(farv)

    绝对中心所属的这条边权值是(w.val),那么有:

    $disu=(w.val+farv-faru){div}2 $

    $disv=(w.val+faru-farv){div}2 $

    上面的问题就引刃而解了,即绝对中心会更加靠近最远的点

    很显然,这一类题目很容易爆int以及用精度写有点浪费,所以干脆全部乘以2,以及后面的dij压边的时候记得全部乘以2即可

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define maxn 505
    #define INF 0x3f3f3f3f3f3f3f3fLL
    #define int ll
    inline ll read()
    {
        ll ans=0;
        char last=' ',ch=getchar();
        while(ch<'0'|ch>'9')last=ch,ch=getchar();
        while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
        if(last=='-')ans=-ans;
        return ans;
    }
    ll min(ll a,ll b)
    {
        return a>b?b:a;
    }
    int n,m,a,b,c;
    ll initu=0;
    int ansu=0,ansv=0;
    ll curans=INF;
    struct E
    {
        int u,v,val;
        E(int a,int b,int c){u=a;v=b;val=c;}
    };
    struct so
    {
        int val;
        int id;
        bool operator<(const so& sec)const
        {
            return val>sec.val;
        }
    }ss[maxn<<2];
    struct dij
    {
        ll val;
        int nxt;
        int from;
        bool operator<(const dij& sec)const
        {
            return val>sec.val;
        }
        dij(ll a,int b,int c){val=a;nxt=b;from=c;}
    };
    vector<E>e;
    vector<int>node[maxn];
    ll dis[maxn][maxn];
    int opt[maxn][maxn];
    ll vis[maxn];
    bool been[maxn];
    priority_queue<dij>heap;
    void getans(int curnum)
    {
        E& cured=e[curnum];
        int u=cured.u;
        int v=cured.v;
        for(int p=1,i=2;i<=n;i++)
        {
            if(dis[v][opt[u][i]]>dis[v][opt[u][p]])
            {
                if(curans>cured.val+dis[u][opt[u][i]]+dis[v][opt[u][p]])
                {
                    curans=cured.val+dis[u][opt[u][i]]+dis[v][opt[u][p]];
                    initu=curans-dis[u][opt[u][i]]*2;
                    ansu=u,ansv=v;
                }
                p=i;
            }
        }
        return;
    }
    signed main()
    {
        n=read(),m=read();
        memset(dis,INF,sizeof(dis));
        for(int i=0;i<=n;i++)dis[i][i]=0;
        for(int i=0;i<m;i++)
        {
            a=read(),b=read(),c=read();
            node[a].push_back(e.size());
            e.emplace_back(a,b,c);
            node[b].push_back(e.size());
            e.emplace_back(b,a,c);
            dis[a][b]=c;
            dis[b][a]=c;
        }
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                {
                    if(dis[i][k]+dis[k][j]<dis[i][j])dis[i][j]=dis[i][k]+dis[k][j];
                }
    
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
                ss[j].id=j,ss[j].val=dis[i][j];
            sort(ss+1,ss+n+1);
            for(int j=1;j<=n;j++)
                opt[i][j]=ss[j].id;
        }
        for(int i=0;i<e.size();i+=2)
        {
            getans(i);
        }
        for(int i=1;i<=n;i++)
        {
            if(dis[i][opt[i][1]]+dis[i][opt[i][2]]<curans)
            {
                curans=dis[i][opt[i][1]]+dis[i][opt[i][2]];
                ansu=ansv=i;
            }
        }
        cout<<curans<<"
    ";
        memset(vis,INF,sizeof(vis));
        vis[ansu]=initu;
        heap.push(dij(vis[ansu],ansu,ansu));
        if(ansu!=ansv)
        {
            vis[ansv]=dis[ansv][ansu]*2-initu;
            heap.push(dij(vis[ansv],ansv,ansu));
        }
        int curcnt=0;
        while(!heap.empty()&&curcnt<n)
        {
            dij curx=heap.top();heap.pop();
            if(been[curx.nxt])continue;
            been[curx.nxt]=1;
            curcnt++;
            if(curx.nxt!=curx.from)cout<<curx.nxt<<" "<<curx.from<<"
    ";
            for(int k:node[curx.nxt])
            {
                int nxt=e[k].v;
                if(!been[nxt]&&vis[curx.nxt]+e[k].val*2<vis[nxt])
                {
                    vis[nxt]=vis[curx.nxt]+e[k].val*2;
                    heap.push(dij(vis[nxt],nxt,curx.nxt));
                }
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    HTML5元素标记释义
    Mvc使用Partial View 来封装上传控件
    订单页过滤,sql写法
    防止提交重复订单的方法
    查询数据库所有列
    asp.net 异常处理
    7. DateTime,TimeSpan
    8.1.thread
    8.2.Task
    2.2. Array
  • 原文地址:https://www.cnblogs.com/et3-tsy/p/13974833.html
Copyright © 2011-2022 走看看