1. 程式人生 > 實用技巧 >2019-2020 XX Open Cup, Grand Prix of Korea

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<<"\n";
	return 0;
}

J.啟發式合併

大致題意經過轉化之後的

給定n個區段【l, r】, 以及對應的權值,注意他們不交叉

現在就是求 k (從1->n分別求) 層,至多疊k層的最大權值和為多少

思路

顯然,我們可以發現他們不交叉只有兩種情況,一種是完全包含,一種是完全不相交

這就有一個樹的特徵了,建樹先排個序,左端第一維(越左越優),區段長第二維(越長越優),再線性掃一遍。

我們把樹建好後,很顯然可以在樹上去算dp,但是會被下圖的特例卡退化成\(O(n^2)\)

那麼我們應該怎麼想這道題嘞?

其實有個很核心的性質

如果我們在第k層進行貪心選擇的時候,k+1層也一定會選上這些

這一點一定要理解好

那麼我們理解了這個之後,只要利用堆,去儲存單層最優的區段貢獻即可(對於兄弟之間而言,他們不會產生影響,所以取出兩邊最優的相加,就是父親的最優)

而這一過程可以利用啟發式合併的思想進行優化處理

對於本題, et3_tsy 有一定的收穫:

對於啟發式合併而言,並不一定要依賴於樹剖,它只是一種思想,就是把小的往大的上面去合併。在進行樹dp的時候,一定優先考慮退化問題(比如這題可能會退化成如下情況)。

#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\n",(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\n",cnt);
		}
	}
	return 0; 
}

I.最小直徑生成樹

這道是一道板子題

求最小直徑生成樹,在oi-wiki上有相關內容

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

再對每個點排序一遍去確定自遠及近的點是誰

下面來確定絕對中心

絕對中心可能是邊,可能是點

對於點來講,它只要選擇最遠的兩個點作為最遠的點,其他點都比他小,相加就是他們的直徑。跑一遍迪傑斯特拉即可

對於邊來說,我們找到絕對中心對應的邊之後,對於它兩個邊上的點u,v初始化不應該都是0,否則就會出現下面的錯誤,。假定我們的絕對中心在4這條藍色邊上,它兩端初始化的dis均是0,對於箭頭指向的點,選綠3,或者紫1,在dij中均是等價的,但是我們很容易發現,在直徑的求解中,如果選紫1顯然它的值會更小(因為它的產生的代價已經由黑3承擔了)

為了處理這個問題,最小直徑生成樹提出了一個思路

它的絕對中心一開始在中間,誰的最遠距離遠就離誰近

如果記絕對中心距離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<<"\n";
    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<<"\n";
        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;
}