【2019CCPC网络选拔赛补题】

HDU6703
题意就不说了,直接分析吧。
对于1操作,a[pos]+1e7,但是我们每次询问是(1,n)范围内的,这个操作显然就是把这个点给删了。
对于2操作,询问[1,r]区间内>=k且不等于a[i] (1<=i<=r) 的最小的数。

题目做法如下:
对位置建权值线段树,每个结点表示一个区间,每个结点维护一个区间的位置的最大值。
(这里有些绕脑,如果看不懂的话需要画图便于理解。)

我们直接看询问,格式是(R,k),线段树是(l,r,rt,k,R),mx[rt]表示rt这个区间的最大位置
如果某一个区间mx[rt]<=R,那么就说明rt里面的每一个数都在[1,R]内,这个显然不符合条件。
将这个剪枝加入query,就可以过这个题目了

#include <bits/stdc++.h>
#define sc(n) scanf("%d",&n)
#define pt(n) printf("%d\n",n)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define vi vector<int>
#define vl vector<long long>
#define pb push_back
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5+7;
int n,m;
int a[maxn],aid[maxn],mx[maxn<<2];
void pushup(int rt)
{
	mx[rt] = max(mx[rt<<1],mx[rt<<1|1]);
}
void build(int l,int r,int rt)
{
	if(l==r)
	{
		mx[rt] = aid[l];
		return;
	}
	int mid = (l+r)/2;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	pushup(rt);
}
void update(int l,int r,int rt,int x)
{
	if(l==r)
	{
		mx[rt] = aid[l];
		return;
	}
	int mid = (l+r)/2;
	if(x<=mid) update(l,mid,rt<<1,x);
	else update(mid+1,r,rt<<1|1,x);
	pushup(rt);
}
int query(int l,int r,int rt,int R,int k)
{	
	if(r<k) return 0;
	if(mx[rt]<=R) return 0;
	if(l==r) return l;
	int mid = (l+r)/2;
	int ans;
	if(mx[rt<<1]>R) ans = query(l,mid,rt<<1,R,k);
	if(ans) return ans;
	return query(mid+1,r,rt<<1|1,R,k);
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",a+i);
			aid[a[i]] = i;
		}
		aid[n+1] = n+1;
		build(1,n+1,1);
		int lans = 0;
		for(int i=0;i<m;i++)
		{
			int op,t1,t2;
			scanf("%d",&op);
			if(op==1)
			{
				scanf("%d",&t1);
				t1^=lans;
				aid[a[t1]] = n+1;
				update(1,n+1,1,a[t1]);
			}
			else 
			{
				//ans = INF;
				scanf("%d%d",&t1,&t2);
				t1^=lans,t2^=lans;
				lans = query(1,n+1,1,t1,t2);
				//lans = ans;
				printf("%d\n",lans);
			}
		}
	}
	return 0;
}

(一句话总结:比赛时这个题目思路不清晰,权值线段树怎么维护一直没想明白,而且死扣这个题,浪费了太多时间。)

HDU6705
题意很简单,不赘述。

先把每个点的出度按权值从小到大排个序。
把询问离线。
然后先把所有的边加进优先队列(起点,第几条边,权值)。
每次扩展可以从当前点取下一条边,和从当前的点取下一个最小的出边。
(这里说的不是很清楚,具体看代码第61到63行)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
struct edge
{
	int to;
	ll w;
	bool operator<(const edge &rhs)const
	{
		return w<rhs.w;
	}
};
vector<edge> G[maxn];
struct node
{
	int s,id;
	ll w;
	bool operator<(const node &rhs)const
	{
		return w>rhs.w;
	}
};
int n,m,k,qy[maxn];
ll ans[maxn];
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(G,0,sizeof(G));
		scanf("%d%d%d",&n,&m,&k);
		for(int i=1;i<=m;i++)
		{
			int x,y;
			ll z;
			scanf("%d%d%lld",&x,&y,&z);
			G[x].push_back({y,z});
		}
		int mx = 0;
		for(int i=1;i<=k;i++) 
		{
			scanf("%d",qy+i);
			mx = max(mx,qy[i]);
		}
		for(int i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
		priority_queue<node> q;
		for(int i=1;i<=n;i++)
		{
			if(G[i].size()) q.push({i,0,G[i][0].w});
		}
		int tot = 0;
		while(true)
		{
			ll w = q.top().w;
			int s = q.top().s,id = q.top().id;
			q.pop();
			ans[++tot] = w;
			if(tot==mx) break;
			if(id+1<(int)G[s].size()) q.push({s,id+1,w-G[s][id].w+G[s][id+1].w});
			int v = G[s][id].to;
			if(G[v].size()) q.push({v,0,w+G[v][0].w});
		}
		for(int i=1;i<=k;i++) printf("%lld\n",ans[qy[i]]);
	}
	return 0;
}

(一句话总结:比赛时看到第k大就往可持久化线段树上想,导致根本无从下手,其实只要离线了,用优先队列可以很简单的维护,而且在CF上做过不少这种题目,思路竟然还是不到位,实在不应该)

发布了159 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/KIKO_caoyue/article/details/100051370