旅游

版权声明:未经过同意不得转载 https://blog.csdn.net/qq_42500298/article/details/83108042

题目描述

暑假,可怜打算去旅游。 在可怜的计划中,可怜一共打算游玩 n 个景点,这些景点被 m 条双向道路联通(即任何两个景点之间都能通过道路直接或者间接到达)。第 i 条道路的长度为 2i。 因为这 n 个景点中,只有 1 号景点在机场附近,所以可怜想要制定一个从 1 号点出发,沿着道路一路游玩,并在最后回到 1 号点的游览计划。同时因为每一条道路都有不一样的风景,于是可怜想要在这个计划中,经过每一条道路至少一次(只要从一个方向走过就算经过过这条道路)。 令一个游览计划的疲劳度为行走长度的总和(多次经过的边长度被多次计算),可怜想要计算所有满足条件的游览计划中疲劳度的最小值。

输入描述:

第一行输入两个整数 n,m 表示景点数和道路数。接下来 m 行每行两个整数 ui,vi 表示第 i 条双向道路连接着 ui,vi。数据保证图中没有重边和自环且联通。 的数据,n,m ≤ 10. 的数据,n ≤ 20. 的数据,n,m ≤ 5000. 的数据,1 ≤ n,m ≤ 5 x 105.

输出描述:

输出一行一个整数,表示疲劳度的最小值。可以证明一定存在满足条件的游览方案。同时答案可能很大,你只需要输出对 998244353 取模后的值。

示例1

输入

4 5
1 2
3 4
2 3
1 3
2 4

输出

70

示例2

输入

6 10
4 6
4 5
3 6
5 2
3 2
1 2
3 4
6 1
2 4
1 3

输出

2132

思路

只有最小生成树上的边的 a[i] 会 >1
证明:使用最小生成树上两点之间的路径一定是图中经过边权最大值最小的 路径可以直接得到
所以只需要求出最小生成树,然后在最小生成树上调整就可以了
代码里还有一些要注意的地方

代码

#include<bits/stdc++.h>
#define ls (x<<1)
#define rs (x<<1|1)
#define mid ((l+r)>>1)
using namespace std;
int const xn=5e5+5,xm=5e5+5,mod=998244353;
int n,m,hd[xn],ct,nxt[xn<<1],to[xn<<1],fa[xn],dep[xn],bin[xn];
int bh[xn],s[xm],ans,dfn[xn],id[xn<<1],son[xn],siz[xn],top[xn],tim,sum[xn<<2];
bool rev[xn<<2];
struct N
{
 int u,v;
}e[xm];
int rd()
{
 int ret=0,f=1;
 char ch=getchar();
 while(ch<'0'||ch>'9')
 {
  if(ch=='-')
   f=0; 
  ch=getchar();
 }
 while(ch>='0'&&ch<='9')
  ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
 return f?ret:-ret;
}
int upt(long long x)
{
 while(x>=mod)
  x-=mod; 
 while(x<0)
  x+=mod; 
 return x;
}
void add(int x,int y,int d)
{
 to[++ct]=y; 
 nxt[ct]=hd[x]; 
 hd[x]=ct; 
 id[ct]=d;
}
int find(int x)
{
 return fa[x]==x?x:fa[x]=find(fa[x]);
}
void dfs(int x,int f)
{
 fa[x]=f; 
 dep[x]=dep[f]+1; 
 siz[x]=1;
 for(int i=hd[x],u;i;
  i=nxt[i])
 if((u=to[i])!=f)
 {
  bh[u]=id[i]; 
  dfs(u,x); 
  if(siz[u]>siz[son[x]])
   son[x]=u;
  siz[x]+=siz[u];
 }
}
void dfs2(int x)
{
 dfn[x]=++tim;
 if(son[x])
  top[son[x]]=top[x],dfs2(son[x]);
 for(int i=hd[x],u;i;i=nxt[i])
 {
  if((u=to[i])==fa[x]||u==son[x])
   continue;
  top[u]=u; 
  dfs2(u);
 }
}
void build(int x,int l,int r)
{
 if(l==r)
 {
  sum[x]=2; 
  return;
 }//MST边初始走2次
 build(ls,l,mid); 
 build(rs,mid+1,r);
 sum[x]=sum[ls]+sum[rs];
}
void pushdown(int x,int l,int r)
{
 if(!rev[x])
  return;
 sum[ls]=3*(mid-l+1)-sum[ls]; 
 rev[ls]^=1;
 sum[rs]=3*(r-mid)-sum[rs]; 
 rev[rs]^=1;
 rev[x]=0;
}
void update(int x,int l,int r,int L,int R)
{
 if(l>=L&&r<=R)
 {
  sum[x]=3*(r-l+1)-sum[x]; 
  rev[x]^=1; 
  return;
 }
 pushdown(x,l,r);
 if(mid>=L)
  update(ls,l,mid,L,R);
 if(mid<R)
  update(rs,mid+1,r,L,R);
 sum[x]=sum[ls]+sum[rs];
}
void work(int x,int y)
{
 while(top[x]!=top[y])
 {
  if(dep[x]<dep[y])
   swap(x,y);
  update(1,1,n,dfn[top[x]],dfn[x]); 
  x=fa[top[x]];
 }
 if(dep[x]<dep[y])
  swap(x,y);
 if(x==y)
  return;//!!!
 update(1,1,n,dfn[y]+1,dfn[x]);//lca之上的那条边不改
}
int query(int x,int l,int r,int pos)
{
 if(l==r)
  return sum[x];
 pushdown(x,l,r);
 if(pos<=mid)
  return query(ls,l,mid,pos);
 else 
  return query(rs,mid+1,r,pos);
}
void init()
{
 bin[0]=1;
 for(int i=1;i<=m;i++)
  bin[i]=upt(bin[i-1]+bin[i-1]);
}
int main()
{
 n=rd(); 
 m=rd(); 
 init();
 for(int i=1;i<=n;i++)
  fa[i]=i;
 for(int i=1,u,v,w;i<=m;i++)
 {
  u=rd(),v=rd(); 
  e[i].u=u; 
  e[i].v=v;
  int x=find(u),y=find(v);
  if(x==y)
   continue;
  add(u,v,i); 
  add(v,u,i);
  fa[x]=y; 
  s[i]=2;
 }
 memset(fa,0,sizeof fa);
 dfs(1,0); 
 top[1]=1; 
 dfs2(1); 
 build(1,1,n);
 for(int i=1;i<=m;i++)
 {
  if(s[i])
   continue;
  s[i]=1; 
  work(e[i].u,e[i].v);
 }
 for(int i=2;i<=n;i++)
  s[bh[i]]=query(1,1,n,dfn[i]);//1无上面的边
 for(int i=1;i<=m;i++)
  ans=upt(ans+(long long)bin[i]*s[i]%mod);
 printf("%d\n",ans);
 return 0;
}

来源:nkw

猜你喜欢

转载自blog.csdn.net/qq_42500298/article/details/83108042