珂朵莉树转化区间(对于多区间类问题)+扫描线线段树维护:1031T3

http://cplusoj.com/d/senior/p/SS231031C

珂朵莉树有个很好的性质:

任意时刻,所有区间都是不交的

所以我们可以把所有区间先拿珂朵莉树变成一堆小区间,每个区间有个存活时间 [ t 1 , t 2 ] [t_1,t_2] [t1,t2]

考虑这个区间意义。他会在 l ∈ [ 1 , t 1 ] , r ∈ [ t 1 , t 2 ] l\in[1,t1],r\in[t1,t2] l[1,t1],r[t1,t2] 的判断区间贡献 l e n len len

考虑回到询问区间。我们把这两种区间放在一起扫描线,然后推下式子再拿线段树维护即可

//4k
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//srand(time(0));
#define N 2000010
//#define M
#define mo 998244353
int pw(int a, int b) {
	int ans=1; 
	while(b) {
		if(b&1) ans*=a; 
		a*=a; b>>=1; 
		ans%=mo; a%=mo; 
	}
	return ans; 
}
void Mod(int &a) { if(a>=mo || a<=-mo) a%=mo; if(a<0) a+=mo; }
void Add(int &a, int b) { a+=b; Mod(a); }
void Mul(int &a, int b) { Mod(b); a*=b; Mod(a); } 
struct node {
	int l, r, t; 
	bool operator < (const node &A) const {
		return l<A.l; 
	}
};
set<node>s; 
struct Node {
	int t1, t2, len; 
};
vector<Node>G; 
int n, m, i, j, k, T;
int l, r, q, ans[N], p[N]; 

struct Segment_tree {
	int sex; 
	int rt, tot, ls[N<<2], rs[N<<2]; 
	int s[N], tag[N]; 
	void build(int &k, int l, int r) {
		if(!k) k=++tot; 
		if(l==r) return ; 
		int mid=(l+r)>>1; 
		build(ls[k], l, mid); build(rs[k], mid+1, r); 
	}
	void jia(int k, int l, int r, int z) {
		Add(tag[k], z); Add(s[k], z*(r-l+1)); 
	}
	void push_down(int k, int l, int r, int mid) {
		jia(ls[k], l, mid, tag[k]); 
		jia(rs[k], mid+1, r, tag[k]); 
		tag[k]=0; 
	}
	void push_up(int k) {
		s[k]=s[ls[k]]+s[rs[k]]; Mod(s[k]); 
	}
	void add(int k, int l, int r, int x, int y, int z) {
		if(l>=x && r<=y) return jia(k, l, r, z), void(); 
		int mid=(l+r)>>1; push_down(k, l, r, mid); 
		if(x<=mid) add(ls[k], l, mid, x, y, z); 
		if(y>=mid+1) add(rs[k], mid+1, r, x, y, z); 
		push_up(k); 
	}
	int que(int k, int l, int r, int x, int y) {
		if(l>=x && r<=y) return s[k]; 
		int mid=(l+r)>>1, sum=0; push_down(k, l, r, mid); 
		if(x<=mid) Add(sum, que(ls[k], l, mid, x, y)); 
		if(y>=mid+1) Add(sum, que(rs[k], mid+1, r, x, y)); 
		push_up(k); 
		return sum; 
	}
}Seg1, Seg2;

void del(set<node>::iterator it) {
	int l=(it->l), r=(it->r), t=(it->t); 
	if(t==0) return ; 
	if(t==i) return ; 
	if(l<0) return ; 
	if(r>=mo) return ; 
	if(r-l+1<=0) return ; 
//	debug("[%d %d] %d %d\n", l, r, t, i-1); 
	G.pb({t, i-1, r-l+1}); 
}

void split(int x) {
	auto it=s.upper_bound({x, 0, 0}); --it; 
	if((it->l)==x) return ; 
	int l=(it->l), r=(it->r), t=(it->t); 
	s.erase(it); 
	s.insert({l, x-1, t}); 
	s.insert({x, r, t}); 
}

bool cmp(Node x, Node y) {	
	if(x.t1==y.t1) return x.len>y.len; 
	return x.t1>y.t1; 
}

signed main()
{
	freopen("range.in", "r", stdin);
	freopen("range.out", "w", stdout);
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
	Seg1.sex=1; Seg2.sex=2; 
	n=read(); q=read(); 
	Seg1.build(Seg1.rt, 1, n); 
	Seg2.build(Seg2.rt, 1, n); 
	s.insert({-1, -1, 0}); s.insert({0, mo-1, 0}); s.insert({mo, mo, 0}); 
	for(i=1; i<=n; ++i) {
		l=read()+1; r=read(); 
		split(r+1); split(l); 
		auto it1=s.upper_bound({l, 0, 0}); --it1; 
		auto it2=s.upper_bound({r+1, 0, 0}); --it2; 
		for(auto it=it1; it!=it2; ++it) del(it); 
		s.erase(it1, it2); 
		s.insert({l, r, i}); 
	}
	while(s.begin()!=s.end()) del(s.begin()), s.erase(s.begin()); 
	for(i=1; i<=q; ++i) {
		l=read(); r=read(); 
		G.pb({l, r, -i}); 
		p[i]=(r-l+1)*(r-l+2)/2; Mod(p[i]); 
//		debug(">> %lld\n", p[i]); 
		p[i]=pw(p[i], mo-2); Mod(p[i]); 
	}
	sort(G.begin(), G.end(), cmp); 	
	for(auto t : G) {
		if(t.len>0) {
			Seg1.add(1, 1, n, t.t1, t.t2, t.len*(t.t1+1)); 
			Seg2.add(1, 1, n, t.t1, t.t2, t.len); 
		}
		else {
			int s1=Seg1.que(1, 1, n, t.t1, t.t2); 
			int s2=Seg2.que(1, 1, n, t.t1, t.t2); 
			Add(ans[-t.len], s1-t.t1*s2); Mod(ans[-t.len]); 
		}
	}
	for(i=1; i<=q; ++i) Mul(ans[i], p[i]); 
	for(i=1; i<=q; ++i) printf("%lld\n", ans[i]); 
	return 0;
}


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值