【问题描述】 公元2044年,人类进入了宇宙纪元。 L国有n个星球,还有n-1条双向航道,每条航道建立在两个星球之间,这n-1
条航道连通了L国的所有星球。
小P掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从ui号星球沿最快的宇航路径飞行到vi号星球去。显然,
飞船驶过一条航道是需要时间的,对于航道j,任意飞船驶过它所花费的时间为tj,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新,L国国王同意小P的物流公司参与L国的航道建设,即允许小P把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小P的物流公司就预接了m个运输计划。在虫洞建设完成后,这m个运输计划会同时开始,所有飞船一起出发。当这m个运输计划都完成时,小P
的物流公司的阶段性工作就完成了。 如果小P可以自由选择将哪一条航道改造成虫洞,试求出小P
的物流公司完成阶段性工作所需要的最短时间是多少?
【输入格式】 输入文件名为 transport.in。 第一行包括两个正整数n、m,表示L国中星球的数量及小P公司预接的运输计划的数量,星球从1到n编号。 接下来n-1行描述航道的建设情况,其中第i行包含三个整数ai, bi和ti,表示第i条双向航道修建在ai与bi两个星球之间,任意飞船驶过它所花费的时间为ti。 接下来m行描述运输计划的情况,其中第j行包含两个正整数uj和vj,表示第j个运输计划是从uj号星球飞往vj号星球。
【输出格式】 输出文件名为transport.out。 共1行,包含1个整数,表示小P的物流公司完成阶段性工作所需要的最短时间。
【数据范围】
对于 20%的数据,m=1。
对于另外 40%的数据,第 i 号小区与第 i+1 号小区相连。
对于以上 60%的数据,保证 0 <= n, m <= 100000
对于 100%的数据,0 <= n, m <= 200000, wi <= 1000,答案保证在
10^9 范围内
先立个flag,在搞图论的期间一定要刚掉这个题!
前天队内胡策,某二组出了运输计划…考场上打了半小时的暴力刚出来了55分。
对于一档数据直接暴力LCA去掉权值最大的一条边即可。
上暴力的代码
#include
#include
#include
#include
#include
using namespace std;
const int maxn=200000+500;
struct Edge
{
int f;
int to;
int d;
int next;
}edge[maxn];
int tot,head[maxn];
void add(int f,int t,int d)
{
edge[++tot].f=f;
edge[tot].d=d;
edge[tot].to=t;
edge[tot].next=head[f];
head[f]=tot;
}
int n,m;
int dist[maxn],deep[maxn],fa[maxn];
void dfs(int f,int t)
{
fa[t]=f;
deep[t]=deep[f]+1;
for(int i=head[t];i;i=edge[i].next)
{
Edge e=edge[i];
if(!deep[e.to])
{
dist[e.to]=e.d;
dfs(t,e.to);
}
}
}
int lca(int x,int y)
{
int tot=0;
int maxdist=0;
if(deep[x]while(deep[x]!=deep[y])
{
tot+=dist[x];
maxdist=max(maxdist,dist[x]);
x=fa[x];
}
while(x!=y)
{
tot+=dist[x];
maxdist=max(maxdist,dist[x]);
x=fa[x];
tot+=dist[y];
maxdist=max(maxdist,dist[y]);
y=fa[y];
}
return tot-maxdist;
}
void solve1()
{
int f,t;
scanf("%d%d",&f,&t);
int ans=lca(f,t);
printf("%d\n",ans);
}
void read(int &a)
{
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
int ans=0;
while(c>='0'&&c<='9')
{
ans*=10;
ans+=c-'0';
c=getchar();
}
a=ans;
return;
}
int cnt[maxn];
int qzh[maxn];
int dis[maxn];
int totd[maxn];
int ll[maxn];
int rr[maxn];
void solve2()
{
int maxx=0;
int ans1;
int poi;
for(int i=1;i1]+dis[i];
}
for(int i=1;i<=m;i++)
{
int f,t;
read(f),read(t);
ll[i]=min(f,t);
rr[i]=max(f,t)-1;
totd[i]=qzh[max(f,t)-1]-qzh[min(f,t)-1];
if(totd[i]>maxx)
{
poi=i;
maxx=totd[i];
ans1=maxx;
}
}
for(int i=ll[poi];i<=rr[poi];i++)
{
int ans=0;
for(int j=1;j<=m;j++)
{
if(rr[j]i)
continue;
ans=max(totd[j]-dis[i],ans);
}
maxx=min(ans,maxx);
}
printf("%d\n",maxx);
}
int main()
{
read(n),read(m);
for(int i=1;iint a,b,c;
read(a),read(b),read(c);
add(a,b,c);
add(b,a,c);
dis[min(a,b)]=c;
}
dfs(100005,1);
if(m==1)
{
solve1();
}
else if(n<=100000&&m<=100000)
{
solve2();
}
else
{
puts("-1");
}
return 0;
}
嗯,下面就是其中一个95分思路。
这个题中:
当这m个运输计划都完成时,小P的物流公司的阶段性工作就完成了
小P的物流公司完成阶段性工作所需要的最短时间。
所以说这个题就是在符合某种条件的情况下求最大值最小。
二分!
直接二分答案。
那么,对于一个mid值,我们该怎么验证呢?
预处理的时候记录一下每一条运输计划的起点,终点,两者的LCA,总长度。
然后以长度为关键字从大到小排序。
我们从1—>m for一遍,直到找到一条运输计划的长度小于mid,记录当前位置poi,如果当前运输计划的长度大于mid,我们就要考虑它所在的路径,怎么维护呢?用树上前缀和,起点和终点++,lca-=2就可以。
处理出来求一遍前缀和,得到的是在这前poi-1条运输计划中,每条边被经过的次数。
我们找到那些被经过poi-1次的边,然后用当前最大的运输计划来减这条边,如果减去之后小于等于mid,直接return false(我这里设的是return false的时候右边界减小)。
正确性呢?
如果我们选出一条不是被这前poi个运输计划经过的边删掉,则不能保证这前poi条运输计划的总长都减小了,而如果减去的边都不在这前poi个运输计划内,则不能保证答案更优….
这个题最后一个点卡常卡的我心累…..
加了各种常数优化,今天早上还学了DQS学长的DFS序求前缀和….最后一个点还是死活TLE掉了。
#include
#include
#include
#include
#include
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int maxn=3e5+500;
int n,m;
struct Edge
{
int to;
int d;
int next;
}edge[maxn<<1];
int head[maxn];
int tot;
inline void add(int f,int t,int d)
{
edge[++tot]=(Edge){t,d,head[f]};
head[f]=tot;
}
int ii;
int dist[maxn],deep[maxn],fa[maxn][30];
inline void dfs1(int f,int t,int dep)
{
deep[t]=dep;
fa[t][0]=f;
for(register int j=1;j<=ii;++j)
fa[t][j]=fa[fa[t][j-1]][j-1];
for(register int i=head[t];i;i=edge[i].next)
{
Edge e=edge[i];
if(e.to==f||deep[e.to])
continue;
dist[e.to]=dist[t]+e.d;
dfs1(t,e.to,dep+1);
}
}
int lca(int x,int y)
{
if(deep[x]for(register int j=ii;j>=0;--j)
if(deep[fa[x][j]]>=deep[y])
x=fa[x][j];
if(x==y)
return x;
for(register int j=ii;j>=0;--j)
if(fa[x][j]!=fa[y][j])
x=fa[x][j],y=fa[y][j];
return fa[x][0];
}
int bq[maxn],qzh[maxn];
struct meico
{
int f;
int t;
int d;
int lca;
}sz[maxn];
int ask_(int x,int y)
{
return deep[x]>deep[y]?x:y;
}
int s[maxn];
int top;
bool can(int mid)
{
for(register int i;i<=n;++i)
qzh[i]=0;
int poi=m+1;
int tot=0;
for(register int i=1;i<=m;++i)
{
meico hah=sz[i];
if(hah.d<=mid)
{
poi=i;
break;
}
else
{
qzh[hah.f]++,qzh[hah.t]++;
qzh[hah.lca]-=2;
tot++;
}
}
if(!tot)
return true;
for(int i=top;i>=0;i--)
{
qzh[fa[s[i]][0]]+=qzh[s[i]];
}
int maxe=0;
for(register int i=1;i<=n;++i)
{
meico hah=sz[i];
if(qzh[i]==poi-1)
{
if(sz[1].d-bq[i]<=mid)
return false;
maxe=max(maxe,bq[i]);
}
}
return true;
}
bool cmp(meico a,meico b)
{
return a.d>b.d;
}
int divv(int r)
{
sort(sz+1,sz+1+m,cmp);
int ans=r;
int l=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(can(mid))
l=mid+1;
else
r=mid-1,ans=min(ans,mid);
}
return ans;
}
inline void read(int &a)
{
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
int ans=0;
while(c<='9'&&c>='0')
ans*=10,ans+=c-'0',c=getchar();
a=ans;
return;
}
queue<int>q;
inline void bfs()
{
q.push(1);
while(!q.empty())
{
int x=q.front();
q.pop();
s[++top]=x;
for(int i=head[x];i;i=edge[i].next)
{
Edge e=edge[i];
if(e.to!=fa[x][0])
q.push(e.to);
}
}
}
int ff[maxn],tt[maxn],dd[maxn];
int main()
{
freopen("transport.in","r",stdin);
freopen("transport.out","w",stdout);
read(n),read(m);
for(ii=1;(1<for(register int i=1;i1,1,1);
int maxx=0;
for(register int i=1;i<=m;++i)
{
read(sz[i].f),read(sz[i].t);
sz[i].lca=lca(sz[i].f,sz[i].t);
sz[i].d=dist[sz[i].f]+dist[sz[i].t]-(dist[sz[i].lca]<<1);
maxx=max(maxx,sz[i].d);
}
for(register int i=1;iprintf("%d",divv(maxx));
fclose(stdin);
fclose(stdout);
return 0;
}