2019 第一试【迅雷】【Alice&Bob】【TJOI2015旅游】20190120

T1【TJOI2015旅游】

WOJ 4322
在这里插入图片描述
在这里插入图片描述

【只能买卖一次哦!!!】

一道树链剖分,不过我还不会,就先跳过。

30 30 30分纯暴力:
计算LCA,分两段算,再算一次两段连在一起的,取MAX

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=50010;
struct edge{int v,nxt;}e[N*2];
int first[N],cnt=0;
inline void add(int u,int v){e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;}
int n,m,pri[N],ans;
int fa[N][18],dep[N];
void predfs(int x,int f){
	fa[x][0]=f;
	for(int i=1;(1<<i)<=dep[x];i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=first[x];i;i=e[i].nxt){
		if(e[i].v==f)continue;
		dep[e[i].v]=dep[x]+1;
		predfs(e[i].v,x);
	}
}
int LCA(int a,int b){
	if(dep[a]<dep[b])swap(a,b);
	int t=dep[a]-dep[b];
	for(int i=0;i<=16;i++)
		if(t&(1<<i))a=fa[a][i];
	if(a==b)return a;
	for(int i=16;i>=0;i--)
		if(fa[a][i]!=fa[b][i]){
			a=fa[a][i];b=fa[b][i];
		}
	return fa[a][0];
}
void in(){
	n=read();
	for(int i=1;i<=n;i++)
		pri[i]=read();
	for(int i=1;i<n;i++){
		int u,v;
		u=read();v=read();
		add(u,v);add(v,u);
	}
	dep[1]=1;
	predfs(1,0);
	m=read();
}
int solve_1(int a,int b,int v){
	int r=2e9;
	while(1){
		if(r<pri[a])ans=max(ans,pri[a]-r);
		if(r>pri[a])r=pri[a];
		if(a==b)break;
		pri[a]+=v;
		a=fa[a][0];
	}
	return r;//最小数 
}
int solve_2(int a,int b,int v){
	int r=0;
	while(1){
		if(r>pri[a])ans=max(ans,r-pri[a]);
		if(r<pri[a])r=pri[a];
		if(a==b)break;
		pri[a]+=v;
		a=fa[a][0];
	}
	return r;//最大数 
}
void work_1(){
	int a,b,k,lca,e,f;
	while(m--){
		a=read();b=read();k=read();
		lca=LCA(a,b);
		ans=0;
		e=solve_1(a,lca,k);
		f=solve_2(b,lca,k);
		pri[lca]+=k;
		printf("%d\n",max(ans,f-e));
	}
}
int main(){
	in();
	work_1();//只能买卖一次的暴力 
	return 0;
}


T2【迅雷】

WOJ 2876
在这里插入图片描述
在这里插入图片描述
唯一 一道考场做出的题,确实有点水,看完题面,就大概知道是类似于 k r u s k a l kruskal kruskal的并查集做法。

输入边,排序,从大到小看。

如果电脑和资源器连通,就结束并输出当前这条边的长度。
【注意每次合并都要把节点信息更新至祖先节点!!!还有别忘了正无穷"+00"哦】

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=100010;
int n,m;
struct edge{int u,v,w;}e[N];
inline int SORT(const edge &x,const edge &y){return x.w>y.w;}
int id[N],fa[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main(){
	n=read();m=read();
	int p,y;
	y=read();
	for(int i=1,u;i<=y;i++){
		u=read();
		id[u]=1;
	}
	for(int i=1;i<=m;i++){
		e[i].u=read();e[i].v=read();e[i].w=read();
	}
	p=read();
	int flag=0;
	for(int i=1,u;i<=p;i++){
		u=read();
		if(id[u]==1)flag=1;
		id[u]=2;
	}
	if(flag){
		printf("+00");
		return 0;
	}
	sort(e+1,e+m+1,SORT);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		int fu=find(e[i].u),fv=find(e[i].v);
		if(fu==fv)continue;
		if(id[fu]+id[fv]==3){
			printf("%d",e[i].w);
			break;
		}
		fa[fv]=fu;
		if(id[fu]!=id[fv])id[fu]=max(id[fu],id[fv]);
	}
	return 0;
}


T3【Alice&Bob】

WOJ 4321
在这里插入图片描述
在这里插入图片描述
刚开始想的,用 a l i c e alice alice直接推 b o b bob bob,但失败了,不知有没有这个关系。
先贴一份 10 10 10分代码,若大佬们有什么想法,发现,可以交流交流。

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}return f==1?x:-x;
}
const int N=100010;
int n,a[N],b,smax[N*4],maxx=0;long long ans=0;
void add(int x,int v){for(;x<=n;x+=x&-x)smax[x]=max(smax[x],v);}
int query(int x){int ret=0;for(;x;x-=x&-x)ret=max(ret,smax[x]);return ret;}
int main(){
	n=read();for(int i=1;i<=n;i++)a[i]=read();
	for(int i=n;i>0;i--){
		if(a[i]==a[i+1])b=maxx+1;else{b=query(a[i])+1;add(a[i],b);}
		maxx=max(maxx,b);ans+=b;
	}
	printf("%lld",ans);
	return 0;
}

正解   贪心+拓扑排序

基本思想是先求出每一个的高度,再算下降子序列。

加了特技的拓扑排序:用 s e t set set,内部自动从小到大排序。
连边(大->小)建图后肯定是一个DAG,就会想拓扑排序。
要令 b o b bob bob序列之和尽量大,就是大的尽量向前,小的尽量向后。

虽然做不来,但是看题解的时候还是很容易,就详见代码吧。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct edge{int v,nxt;}e[N<<1];
int first[N],cnt=0;
int n,a[N],in[N],hei[N],last[N];
inline void add(int u,int v){e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;in[v]++;}
set<int>s;//有序的 从小到大 
void topsort(){
	for(int i=1;i<=n;i++)
		if(!in[i])s.insert(i);//节点编号从小到大,前面的可以贪心比后面高些,这样下降子序列更长
	int t=0;
	while(!s.empty()){
		int u=*s.begin();s.erase(s.begin());//加入,重新排序,一直弹出最小的
		//没限制,就可以弹最小的 
		hei[u]=n-(t++);
		for(int i=first[u];i;i=e[i].nxt){
			in[e[i].v]--;
			if(!in[e[i].v])s.insert(e[i].v);
		}
	}
}
long long work(){//开long long
	topsort();
	long long ans=1;//n位置的答案 
	int len=1;
	a[1]=hei[n];
	for(int i=n-1;i;i--){
		int x=hei[i];
		if(x>a[len]){
			a[++len]=x;
			ans+=len;
		}
		else{
			int pos=lower_bound(a+1,a+len+1,x)-a;
			//大于或等于,但hei值不重复,就可以不用upper_bound(…) 
			a[pos]=x;
			ans+=pos;
		}
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		if(last[a[i]])add(last[a[i]],i);//大的连向小的 
		if(a[i]>1)add(i,last[a[i]-1]);//注意是i不是a[i],是位置不是值 
		last[a[i]]=i;
	}
	printf("%lld",work());
	return 0;
}

~ \ ( ≧ ▽ ≦ ) / ~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值