P4149 [IOI2011]Race

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4149 [IOI2011]Race相关的知识,希望对你有一定的参考价值。

P4149 [IOI2011]Race

题意:

给一棵树,每条边有权。求一条简单路径,权值和等于 k,且边的数量最小。

题解:

用t[i]:长度为i的路径包含的最少边数
按照子树顺序,依次用dep[u]+t[K-d[u]]更新ans,更新t数组,最后遍历子树恢复t数组

代码:

85分代码,不知道哪错了

//#pragma optimize("Ofast")
#include <bits/stdc++.h>
#define MAXN 4000005
#define MAXK 10000007
#define inf 20000007
using namespace std;
typedef long long ll;

int N, K;
struct edge
{
    int v;
    ll w;
    edge(int v= 0, ll w= 0) : v(v), w(w)
    {
    }
};

vector<edge> adj[MAXN];

int vis[MAXN];
ll d[MAXN];
int sz[MAXN];
int rt, cnt;
int t[MAXK], ans= inf;
int dep[MAXN];
void dfs_rt(int u, int fa, int tot)
{
    //++cnt;
    sz[u]= 1;
    int v, w, n= 0;
    for (int k= 0; k < adj[u].size(); k++) {
        v= adj[u][k].v;
        if (v == fa || vis[v])
            continue;
        dfs_rt(v, u, tot);
        sz[u]+= sz[v];
        n= max(n, sz[v]);
    }
    n= max(n, tot - sz[u]);
    if (2 * n <= tot)
        rt= u;
}

void dfs1(int u, int fa)
{
    ++cnt;
    if (K >= d[u])
        ans= min(ans, dep[u] + t[K - d[u]]);
    int v, w;
    for (int k= 0; k < adj[u].size(); k++) {
        v= adj[u][k].v;
        w= adj[u][k].w;
        if (v == fa || vis[v])
            continue;

        d[v]= d[u] + w;
        dep[v]= dep[u] + 1;
        dfs1(v, u);
    }
}

void dfs2(int u, int fa, int flag)
{
    if (K >= d[u]) {
        if (flag == 1)
            t[d[u]]= min(t[d[u]], dep[u]);
        else
            t[d[u]]= inf;
    }

    int v;
    for (int k= 0; k < adj[u].size(); k++) {
        v= adj[u][k].v;
        if (v == fa || vis[v])
            continue;
        dfs2(v, u, flag);
    }
}
void work(int u, int fa, int tot)
{
    dfs_rt(u, fa, tot);
    u= rt;
    vis[u]= 1;
    d[u]= 0;

    t[0]= 1;
    int v, w;
    for (int k= 0; k < adj[u].size(); k++) {
        v= adj[u][k].v;
        w= adj[u][k].w;
        if (vis[v])
            continue;
        cnt= 0;
        d[v]= w; //路径
        dep[v]= 1;
        dfs1(v, u);
        sz[v]= cnt;
        dfs2(v, u, +1);
    }

    dfs2(u, 0, -1);

    for (int k= 0; k < adj[u].size(); k++) {
        v= adj[u][k].v;
        if (vis[v])
            continue;
        work(v, u, sz[v]);
    }
}

int main()
{
    scanf("%d%d", &N, &K);
    memset(t, inf, sizeof(t));
    int u, v, w;
    for (int i= 1; i < N; i++) {
        scanf("%d%d%d", &u, &v, &w);
        u++;
        v++;
        adj[u].push_back(edge(v, w));
        adj[v].push_back(edge(u, w));
    }
    memset(t, inf, sizeof t);

    t[0]= 1;
    work(1, 0, N);

    printf("%d\\n", ans >= N ? -1 : ans);
    return 0;
}

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=200020,maxk=1000100;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
	char ch=getchar();int x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,k,el,head[maxn],to[maxn*2],w[maxn*2],nxt[maxn*2];
int rt,tot,sz[maxn],son[maxn],mine[maxk],ans=INT_MAX,dis1[maxn],dis2[maxn],dl;
bool vis[maxn];
inline void add(int u,int v,int w_){
	to[++el]=v;w[el]=w_;nxt[el]=head[u];head[u]=el;
}
void getrt(int u,int f){	//求重心
	sz[u]=1;son[u]=0;
	for(int i=head[u];i;i=nxt[i]){
    	//v=to[i]不是必需的,可以去掉
		if(to[i]==f || vis[to[i]]) continue;
		getrt(to[i],u);
		sz[u]+=sz[to[i]];son[u]=max(son[u],sz[to[i]]);
	}
	son[u]=max(son[u],tot-sz[u]);
	if(son[u]<son[rt]) rt=u;
}
void getdis(int u,int f,int d1,int d2){	//dfs将子树的信息记录下来(d1是权值和,d2是边数)
	if(d1>k) return;
	dis1[++dl]=d1;dis2[dl]=d2;
	for(int i=head[u];i;i=nxt[i]){
    	//同上
		if(to[i]==f || vis[to[i]]) continue;
		getdis(to[i],u,d1+w[i],d2+1);
	}
}
void getans(int u){	//计算经过u的路径的答案
	mine[0]=0;dl=0;	//mine[0]是0!因为一个端点是u的路径也要考虑,而且0不会被其它子树记录到
	for(int i=head[u];i;i=nxt[i]){
		if(vis[to[i]]) continue;
		int pdl=dl;	//前面的子树有多少元素
		getdis(to[i],u,w[i],1);	//注意调用时w[i]和1
		FOR(j,pdl+1,dl) ans=min(ans,mine[k-dis1[j]]+dis2[j]);
        //更新答案
		FOR(j,pdl+1,dl) mine[dis1[j]]=min(mine[dis1[j]],dis2[j]);
        //更新桶
	}
	FOR(i,1,dl) mine[dis1[i]]=1e9;
}
void getall(int u){	//点分治主过程
	vis[u]=true;
	getans(u);
	for(int i=head[u];i;i=nxt[i]){
		if(vis[to[i]]) continue;
		tot=sz[to[i]];rt=0;
		getrt(to[i],u);
		getall(rt);
	}
}
int main(){
	n=read();k=read();
	FOR(i,1,n-1){
		int u=read()+1,v=read()+1,w=read();	//编号从0开始
		add(u,v,w);add(v,u,w);
	}
	son[0]=(tot=n)+1;	//son[0]设为INF
	getrt(1,0);
	MEM(mine,0x3f);
	getall(rt);
	printf("%d\\n",ans>=n?-1:ans);	//最短长度超过n就是无解
}

以上是关于P4149 [IOI2011]Race的主要内容,如果未能解决你的问题,请参考以下文章

P4149 [IOI2011]Race

「Luogu P4149」「IOI2011」Race

「Luogu P4149」「IOI2011」Race

P4149 [IOI2011]Race 点分治

P4149 [IOI2011]Race

bzoj2599: [IOI2011]Race