SPOJ1825--点分治

Description
After the success of 2nd anniversary (take a look at problem FTOUR for more details), this 3rd year, Travel Agent SPOJ goes on with another discount tour.

The tour will be held on ICPC island, a miraculous one on the Pacific Ocean. We list N places (indexed from 1 to N) where the visitors can have a trip. Each road connecting them has an interest value, and this value can be negative (if there is nothing interesting to view there). Simply, these N places along with the roads connecting them form a tree structure. We will choose two places as the departure and destination of the tour.

Since September is the festival season of local inhabitants, some places are extremely crowded (we call them crowded places). Therefore, the organizer of the excursion hopes the tour will visit at most K crowded places (too tiring to visit many of them) and of course, the total number of interesting value should be maximum.

Briefly, you are given a map of N places, an integer K, and M id numbers of crowded place. Please help us to find the optimal tour. Note that we can visit each place only once (or our customers easily feel bored), also the departure and destination places don’t need to be different.
题目大致是这个意思:
给定一棵含有 N 个结点的带权树,其中结点分为两类,黑点和白
点。
要求找到一条路径,使得经过的黑点数不超过 K 个,且路径长度
最大。
数据范围:
N<=200000
具体可参见国家队09年论文漆子超。首先它是个点分治这没有异议,题目中最大的难点就是如何在合并答案时避免取到同一子树中的两个点,其实要避免也很简单,就是开两个数组g[],d[],g[i]表示之前已经刷过的子树中经过了i个黑点的最优解,d[i]表示当前子树中经过了i个黑点的最优解,就可以实现每次O(当前子树节点个数)的扫答案和修正g数组。但是这样做有一个问题,由于每次都是O(节点个数),这样,每次都扫这么多次,复杂度就不靠谱了,我们发现,答案与遍历子树的个数并没有什么关系,所以就先按每个子树中可以经过的最多的黑点数由小到大排序,这样就可以保证每次扫答案的复杂度加起来不会超过O(节点个数)。
代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 200006
using namespace std;
struct data{
    int maxd,p,sid;
    bool operator <(const data&b)const{
        return maxd<b.maxd;
    }
}a[maxn];
int n,K,m,tot,sum,core,Min,a0,t0,
    lnk[maxn],nxt[maxn*2],son[maxn*2],w[maxn*2],num[maxn],f[maxn],blk[maxn],id[maxn],tmp[maxn];
int ans,g[maxn],d[maxn],b[maxn];
bool vis[maxn],isb[maxn];
void add(int x,int y,int z){
    nxt[++tot]=lnk[x];son[tot]=y;w[tot]=z;lnk[x]=tot;
}
void get_core(int x,int fa){
    num[x]=1;f[x]=0;
    for(int j=lnk[x];j;j=nxt[j]) if((son[j]!=fa)&&(vis[son[j]])){
        get_core(son[j],x);
        num[x]+=num[son[j]];f[x]=max(f[x],num[son[j]]);
    }
    f[x]=max(f[x],sum-num[x]);
    if(f[x]<Min)Min=f[x],core=x;
}
void get_max_d(int x,int fa,int cnt){
    a[a0].maxd=max(a[a0].maxd,cnt);
    for(int j=lnk[x];j;j=nxt[j]) if((vis[son[j]])&&(son[j]!=fa))get_max_d(son[j],x,cnt+isb[son[j]]);
}
void get_d(int x,int fa,int cnt,int p){
    if(p>d[cnt])d[cnt]=p,tmp[++t0]=cnt;
    for(int j=lnk[x];j;j=nxt[j]) if((vis[son[j]])&&(son[j]!=fa))
     get_d(son[j],x,cnt+isb[son[j]],p+w[j]);
}
void dfs(int x){
    vis[x]=0;a0=0;
    for(int j=lnk[x];j;j=nxt[j]) if(vis[son[j]]){
        a[++a0].p=son[j];a[a0].maxd=0;a[a0].sid=j;
        get_max_d(son[j],son[j],isb[son[j]]);
    }
    sort(a+1,a+1+a0);
    for(int i=1;i<=a0;i++)g[a[i].maxd]=-1e9;g[0]=0;
    int tem=0;
    for(int i=1;i<=a0;i++){
        t0=0;
        get_d(a[i].p,a[i].p,isb[a[i].p],w[a[i].sid]);
        b[0]=d[0];
        for(int j=0;j< a[i].maxd;j++)b[j+1]=max(d[j+1],b[j]);
        int k=min(tem,K-isb[x]);
        for(int j=0;j<=k;j++)ans=max(ans,g[j]+b[K-isb[x]-j]);
        tem=a[i].maxd;
        for(int j=0;j<=tem;j++)g[j]=max(g[j],d[j]);
        for(int j=1;j<=t0;j++)d[tmp[j]]=-1e9;
        for(int j=0;j<=a[i].maxd;j++)b[i]=-1e9;
    }
    for(int j=lnk[x];j;j=nxt[j]) if(vis[son[j]]){
        sum=num[son[j]];Min=1e9;get_core(son[j],son[j]);
        dfs(core);
    }
}
int _read(){
    char ch=getchar();int sum=0,p;
    while((!(ch>='0'&&ch<='9'))&&(ch!='-'))ch=getchar();
    if(ch=='-')p=-1,ch=getchar();else p=1;
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=getchar();
    return sum*p;
}
int main(){
    freopen("tour.in","r",stdin);
    freopen("tour.out","w",stdout);
    n=_read();K=_read();m=_read();
    memset(isb,0,sizeof(isb));memset(b,192,sizeof(b));
    for(int i=1;i<=m;i++)isb[_read()]=1;
    for(int i=1,x,y,z;i<n;i++)x=_read(),y=_read(),z=_read(),add(x,y,z),add(y,x,z);
    memset(vis,1,sizeof(vis));
    sum=n;Min=1e9;get_core(1,1);blk[core]=isb[core];
    dfs(core);
    printf("%d",ans);
    return 0;
}

当然,如果你很认真的学习了漆子超的论文,并且弄懂了这题,你仍然不能AC,因为这题刷重心时DFS深度太大,在SPOJ上是会爆栈的,是需要写手工栈的(虽然我本人也没写手工栈)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值