『毒瘤算法系列8』构造序列(线段树优化建图·拓扑DP)

给定序列构造问题,利用线段树优化建立大小关系图,通过拓扑排序进行动态规划赋值。文章详细介绍了如何在大数据量下,通过线段树减少边的数量并实现高效解法,同时给出了无解情况的判断标准。
摘要由CSDN通过智能技术生成

P r o b l e m \mathrm{Problem} Problem

给定一个长度为 n n n的正整数序列 a a a,每个数都在 1 1 1 1 0 9 10^9 109范围内,告诉你其中 s s s个数,并给出 m m m条信息,每条信息包含三个数 l , r , k l,r,k l,r,k以及接下来 k k k个正整数,表示 a [ l ] , a [ l + 1 ] , … , a [ r − 1 ] , a [ r ] a[l],a[l+1],…,a[r-1],a[r] a[l],a[l+1],,a[r1],a[r]里这 k k k个数中的任意一个都比任意一个剩下的 r − l + 1 − k r-l+1-k rl+1k个数大(严格大于,即没有等号)。请任意构造出一组满足条件的方案,或者判断无解。

第一行包含三个正整数 n , s , m n,s,m n,s,m。接下来 s s s行,每行包含两个正整数 p [ i ] , d [ i ] p[i],d[i] p[i],d[i],表示已知 a [ p [ i ] ] = d [ i ] a[p[i]]=d[i] a[p[i]]=d[i],保证 p [ i ] p[i] p[i]递增。接下来 m m m行,每行一开始为三个正整数 l [ i ] , r [ i ] l[i],r[i] l[i],r[i]

k [ i ] ( 1 ≤ l [ i ] ≤ r [ i ] ≤ n , 1 ≤ k [ i ] ≤ r [ i ] − l [ i ] ) k[i](1≤l[i]\le r[i]≤n,1≤k[i]≤r[i]-l[i]) k[i]1l[i]r[i]n1k[i]r[i]l[i],接下来 k [ i ] k[i] k[i]个正整数 x [ 1 ] , x [ 2 ] , . . . , x [ k [ i ] ] ( l [ i ] ≤ x [ 1 ] ≤ x [ 2 ] ≤ … ≤ x [ k [ i ] ] ≤ r [ i ] ) x[1],x[2],...,x[k[i]](l[i]≤x[1]\le x[2]\le …\le x[k[i]]≤r[i]) x[1],x[2],...,x[k[i]]l[i]x[1]x[2]x[k[i]]r[i],表示这 k [ i ] k[i] k[i]个数中的任意一个都比任意一个剩下的 r [ i ] − l [ i ] + 1 − k [ i ] r[i]-l[i]+1-k[i] r[i]l[i]+1k[i]个数大。

对于100%的数据, 1 ≤ s ≤ n ≤ 100000 , 1 ≤ m ≤ 200000 , Σ k ≤ 300 , 000 1≤s≤n≤100000,1≤m≤200000,Σk≤300,000 1sn1000001m200000Σk300,000

S o l u t i o n \mathrm{Solution} Solution

观察到数据范围, ∑ k ≤ 3 × 1 0 5 \sum k\le 3\times 10^5 k3×105,我们需要考虑一个 O ( k ) O(k) O(k)的算法。

我们知道,我们需要用一个很好的算法来确立大小关系,可以用大数向小数连边,再用拓扑徐进行赋值即可。但这道题目的瓶颈在于,连的边太多了,复杂度不允许。怎么办呢?

  • 我们观察到向一个区间连边,我们可以使用线段树优化建图的方式。
  • 实质上很简单,就是我们纯粹的向线段树上对应的区间最上面一层的点连边即可。
    在这里插入图片描述

Just like this, p p p就是线段树对应着一个区间的节点,这样我们就成功完成了建图。

考虑如何进行赋值,我们设当前数值为 f x f_x fx且存在边 ( x , y , v ) (x,y,v) (x,y,v).

  • y y y为定值且 f x − v < a y f_x-v<a_y fxv<ay,则无解。
  • 否则令 f y = min ⁡ ( f x − v ) f_y=\min(f_x-v) fy=min(fxv)
  • 由于 f f f数组处于不断递减的状态,则我们令所有入度为 0 0 0的点 f i = 0 f_i=0 fi=0.
  • 无解的判定:存在 f i < 1    o r    f i > 1 0 9 f_i<1\mathrm{\ \ or\ \ }f_i>10^9 fi<1  or  fi>109

C o d e \mathrm{Code} Code

#include <bits/stdc++.h>

using namespace std;
const int N = 2e6;

int n, cnt(0), s, m, root;
int L[N], R[N], in[N], f[N], lc[N], rc[N], Num[N], pos[N], vis[N];
vector < pair < int, int > > a[N];

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (c < '0' || c > '9') w |= c == '-', c = getchar();
	while (c >= '0' && c <= '9') s = s * 10 + c - 48, c = getchar();
	return w ? -s : s;	
}

void build(int &p,int l,int r)
{
	if (l == r) {
		p = l;
		L[p] = l, R[p] = r;
		return;
	}
	p = ++ cnt;
	L[p] = l, R[p] = r;
	int mid = l + r >> 1;
	build(lc[p],l,mid);
	build(rc[p],mid+1,r);
	a[p].push_back({lc[p],0}), in[lc[p]] ++;
	a[p].push_back({rc[p],0}), in[rc[p]] ++;
}

void MakeE(int &p,int l,int r,int x)
{
	if (l <= L[p] && R[p] <= r) {
		a[x].push_back({p,1});
		in[p] ++;
		return;
	}
	int mid = L[p] + R[p] >> 1;
	if (l <= mid) MakeE(lc[p],l,r,x);
	if (r > mid) MakeE(rc[p],l,r,x);
}

void TopSort(void)
{
	queue < int > q;
	for (int i=1;i<=cnt;++i)
	{
		if (in[i] == 0) q.push(i);
		if (Num[i]) f[i] = Num[i];
		else f[i] = 1e9;
	}
	while (q.size())
	{
		int x = q.front(); q.pop();
		vis[x] = 1;
		for (int i=0;i<a[x].size();++i)
		{
			int y = a[x][i].first; 
			int v = a[x][i].second;
			in[y] --;
			if (Num[y] && f[x] - v < f[y])
				return puts("NIE"), void();
			f[y] = min(f[y],f[x]-v);
			if (f[y] < 1) return puts("NIE"), void();
			if (in[y] == 0) q.push(y);
		}
	}
	for (int i=1;i<=n;++i) 
		if (vis[i] == 0) return puts("NIE"), void();
	puts("TAK");
	for (int i=1;i<=n;++i) printf("%d ", f[i]);
}

int main(void)
{
	n = cnt = read();
	s = read(), m = read();
	build(root,1,n);
	for (int i=1;i<=s;++i)	{
		int x = read(), v = read();
		Num[x] = v;
		if (x < 1 || x > 1e9) 
			return puts("-1"), 0;
	}
	for (int i=1;i<=m;++i)
	{
		cnt ++;
		int l = read(), r = read(), k = read();
		for (int j=1;j<=k;++j) {
			pos[j] = read();
			a[pos[j]].push_back({cnt,0});
			in[cnt] ++;
		}
		if (pos[1] > l) MakeE(root,l,pos[1]-1,cnt);
		if (pos[k] < r) MakeE(root,pos[k]+1,r,cnt);
		for (int i=2;i<=k;++i)
			if (pos[i-1] + 1 < pos[i]) 
				MakeE(root,pos[i-1]+1,pos[i]-1,cnt);
	}
	TopSort();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值