CodeVS第二次月赛 B

题目描述 Description

切记不要加任何文件读写, 本次比赛暂不支持ifndef ONLINE_JUDGE。请互相转告。评测机是linux

 是个富有诗意的男孩。在他居住的小镇上,有 n个村庄,由 n-1条道路连接着。这里的生活和谐而又宁静。

有一天 tty偶然阅读到一篇杂志上的文章《阳光下的向日葵》,深受感动。特别是其中一位小女孩的故事让他记忆犹新。 于是他买来葵花籽,决定也像那位小女孩一样,给村民播种向日葵,把美丽和阳光带给村庄里的每一个人。

于是 tty决定在每次播种前选择两个村庄,然后从其中一个村庄出发,在这两个村庄的道路上的每一个村庄(包括起始点)种上一颗向日葵,在编号 i的村庄播种在可以长出 wi个葵花籽,而且向日葵受到天神 Owaski的保护,在生长过程中不会被人采摘。

若干天后,待到向日葵成熟时,tty又会以相同的路径去采摘那些向日葵上的葵花籽, 无论是否采摘,每一次播种的向日葵都会在下一次播种前死亡。

但是有轻微强迫症的他,不会采摘比他采摘的上一朵葵花籽相等或者更少的向日葵。

而tty并不知道这些数据,只能凭着感觉开始采摘,他想在更多的向日葵上采摘葵花籽,你能告诉他可以最多采摘的向日葵棵数吗?

题解:n个点n-1条边显然是一棵树嘛,所以每个询问求的是树上两点之间权值序列的LIS。

考场上我先是转有根树,求LCA然后暴力找路径nlogn求LIS(其实不需要这么麻烦),结果爆0了。

正确的姿势应该是离线算法,也就是预处理出答案ans[i][j],询问就O(1)输出。

首先枚举每个点,DFS求出这个点与其它点的LIS。这就是加点地求LIS。如果这个点的权值大于栈顶元素的话直接压栈更新答案。否则二分查找第一个权值大于等于它的并将其替换再维护答案。回溯回来要记得改回。这样的时间复杂度是O(n^2logn+Q)的。记得无向图边数要乘2。

%%%__debug大神。

#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define INF 0x7fffffff/10
#define x first
#define y second
using namespace std;
const int MAXN=4002;
int first[MAXN],next[MAXN],w[MAXN],e=0,v[MAXN],n,q=0,f[MAXN];
int top=0,ans[MAXN][MAXN],d[MAXN];
struct point{
	int x,y;
	point(int x=0,int y=0):x(x),y(y){}
	bool operator <(const point& rhs)const {return x<rhs.x;}
}sta[MAXN];
void add(int a,int b)
{
    e++;next[e]=first[a];first[a]=e;v[e]=b;
}
void dfs(int u,int fa,int from,int last)
{
    for(int i=first[u];i!=-1;i=next[i])
    {
        int to=v[i],ty=0,lc,la,lb,anss=last;
        if(to==fa)continue;
        if(from==2&&to==7)
           to=7;
        if(w[to]>sta[top].x)
        {
        	anss=max(anss,top+1);
        	f[to]=++top;
        	sta[top].y=to;
        	sta[top].x=w[to];
        	ty=1;
        }
        else
        {
        	int k=lower_bound(sta+1,sta+top+1,point(w[to],to))-sta;
        	f[to]=k;
        	anss=max(anss,k);
        	lc=k;la=sta[k].x;lb=sta[k].y;
        	sta[k].x=w[to];
        	sta[k].y=to;
    	}
    	ans[from][to]=anss;
        dfs(to,u,from,anss);    
        if(ty)top--;
        else
        {
        	sta[lc].x=la;
        	sta[lc].y=lb;
        }
    }
}
int main()
{
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
    scanf("%d",&n);
    for(int i=0;i<=n;i++)first[i]=-1;
    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    for(int i=1;i<=n;i++)
    {
    	top=1;
    	ans[i][i]=1;
        sta[top].y=i;
        sta[top].x=w[i];
    	dfs(i,-1,i,1);
    }
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
    	int a,b;
    	scanf("%d %d",&a,&b);
    	printf("%d\n",ans[a][b]);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值