题目:
题解:
首先是-1的情况,如果军队数小于1节点的儿子数就-1
这种解一看没法直接求的就二分啊
我的r设成从根节点引出的最长链+根节点到ta儿子节点的最长链(比起那些无脑5000000好科学啊)
具体怎么判断二分的数值能不能达到呢?
在这些时间内军队可以一起行动,那我让所有的军队都往上蹦,蹦到根节点还有剩余时间的记录下来,顺便记录从这个节点(根节点的哪个儿子)出来的剩余时间的最小值及其军队序号
dfs找出来蹦完之后还有哪个边境城市没有驻守,则需要走到从这个边境城市出发向首都走所需 经过的最后一个点(ta所属的 根节点儿子),按这个点到首都距离从大到小排序
然后把剩余的按照时间从大到小排序,如果有从这个(根节点的儿子节点)出来的就直接用ta好了,这里的原理是贪心!因为剩余时间大的就是要匹配距离大的,可以留下剩余时间多的,用从自己出来的军队何乐而不为?
我的题解描述真是鬼畜啊
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define sz 19
using namespace std;
const int N=50005;
int tot,nxt[N*2],point[N],v[N*2],c[N*2],n,m,mi[sz],dis[N][sz],f[N][sz],inc[N],num,ez;
int h[N],son[N],dg[N],a[N],l,r,maxx,restmin[N],restzb[N];
bool vis[N],viv[N],pq[N];
struct hh{int id,tim;}sy[N],jl[N];
int cmp(hh a,hh b){return a.tim>b.tim;}
void addline(int x,int y,int z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void dfs(int x,int fa,int id)
{
h[x]=h[fa]+1;
for (int i=1;i<sz;i++)
if (h[x]<mi[i]) break;
else f[x][i]=f[f[x][i-1]][i-1],dis[x][i]=dis[x][i-1]+dis[f[x][i-1]][i-1];
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
dg[v[i]]=dg[x]+c[i];
if (!fa) id++,son[id]=v[i],maxx=max(maxx,c[i]);
r=max(r,dg[v[i]]);
inc[v[i]]=son[id];
f[v[i]][0]=x;
dis[v[i]][0]=c[i];
dfs(v[i],x,id);
}
}
int jump(int x,int tim)
{
for (int i=sz-1;i>=0;i--)
if (f[x][i] && dis[x][i]<=tim)
tim-=dis[x][i],x=f[x][i];
return x;
}
void ss(int x,int fa,int id,bool bai)
{
vis[x]=bai || vis[x];int size=0;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
size++;
if (x==1) id++;
if (vis[x]) ss(v[i],x,id,1);
else ss(v[i],x,id,0);
}
if (!size && !vis[x] && !viv[id]) viv[id]=1,jl[++num].id=son[id],jl[num].tim=dg[son[id]];
//没有被覆盖的叶子,把ta属于的子树记录下来
}
bool check(int tim)
{
memset(vis,0,sizeof(vis));
memset(viv,0,sizeof(viv));
memset(pq,0,sizeof(pq));
memset(restmin,0x7f,sizeof(restmin));
memset(restzb,0,sizeof(restzb));
num=0;
int cnt=0,i,j=1;
for (i=1;i<=m;i++)
if (dg[a[i]]<=tim)
{
sy[++cnt].id=i;
sy[cnt].tim=tim-dg[a[i]];
int x=inc[a[i]];//x表示所属子树的节点号
if (!restzb[x] || sy[cnt].tim<restmin[x])
restzb[x]=i,restmin[x]=sy[cnt].tim;
}
else vis[jump(a[i],tim)]=1;
ss(1,0,0,0);
sort(jl+1,jl+num+1,cmp);
sort(sy+1,sy+cnt+1,cmp);
pq[0]=1;
for (i=1;i<=num;i++)
{
if (!pq[restzb[jl[i].id]]) {pq[restzb[jl[i].id]]=1;continue;}
while (j<=cnt && (pq[sy[j].id] || sy[j].tim<jl[i].tim)) j++;
if (j>cnt) return 0;
pq[sy[j].id]=1;
}
return 1;
}
int main()
{
int i,j;
mi[0]=1;for (i=1;i<sz;i++) mi[i]=mi[i-1]*2;
scanf("%d",&n);
for (i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addline(x,y,z);
if (x==1 || y==1) ez++;
}
dfs(1,0,0); r+=maxx;
scanf("%d",&m);
if (ez>m) {printf("-1");return 0;}
for (i=1;i<=m;i++) scanf("%d",&a[i]);
int ans=0;
while (l<=r)
{
int mid=(l+r)>>1;
if (check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",ans);
}
吐槽:
真是一道充满挫折的题目:
一开始想了40mins想出正解,然后开写后死也编译不过,UPD 1天后把一个叫做search的过程改成ss就编译过了(╯‵□′)╯︵┻━┻
然后一交90pts,woccccc你还卡常?
卡了一波卡不进去,这居然不是卡常能解决的问题。。。。。
又学习了check中一种比较科学的方法才AC
但是112ms,除了打表的就是我快啊hahaha