Intersection and Union

博客内容讨论了一种区间操作的问题,其中涉及并集、交集和异或操作。作者提出了动态规划的方法,通过矩阵快速幂来计算每个位置的贡献,并使用线段树进行优化。文章介绍了矩阵转移的思路,以及如何利用线段树维护矩阵,最后给出了两种不同实现的代码示例。
摘要由CSDN通过智能技术生成

题意

给n个区间S_i = [ l i , r i ] [l_i,r_i] [li,ri],n-1个集合操作 o p i = { ∪ , ∩ , ⊕ } op_i=\{\cup,\cap,\oplus\} opi={,,}.求
∣ ( . . . ( ( S 1    o p 1    S 2 )    o p 3    S 3 ) . . . )    o p n − 1    S n ∣ |(...((S_1\;op_1\;S_2)\;op_3\;S_3)...)\;op_{n-1}\;S_{n}| (...((S1op1S2)op3S3)...)opn1Sn
即所有可能操作结果的区间长度总和

constrains:
1 ≤ n ≤ 3 e 5 1\leq n\leq 3e5 1n3e5
0 ≤ l i ≤ r i ≤ 3 e 5 0\leq l_i\leq r_i\leq3e5 0liri3e5

思路

考虑独立贡献,数轴上每一个点都是可以贡献的单位,比如, x x x这一点的贡献为 1 ∗ ( x 这一点在结果区间的方案数 ) 1*(x这一点在结果区间的方案数) 1(x这一点在结果区间的方案数),把包含x的区间记为1,否则为0,例如[0,0,0,1,1,1,1,0]中8个区间中4~7的区间包含 x x x,方案数可以用 d p ( i , j ) dp(i,j) dp(i,j)简单计算, d p ( i , j ) dp(i,j) dp(i,j)表示操作完前 i i i个区间时,结果中 x x x位为 j ∈ { 0 , 1 } j\in \{0,1\} j{0,1}的方案数.有如下线性转移
若s[i]包含 x x x, d p ( i , 1 ) = 2 d p ( i − 1 , 0 ) + 2 d p ( i − 1 , 1 ) , d p ( i , 0 ) = d p ( i − 1 , 0 ) + d p ( i − 1 , 1 ) dp(i,1)=2dp(i-1,0)+2dp(i-1,1),dp(i,0)=dp(i-1,0)+dp(i-1,1) dp(i,1)=2dp(i1,0)+2dp(i1,1),dp(i,0)=dp(i1,0)+dp(i1,1)
若不包含, d p ( i , 1 ) = 2 d p ( i − 1 , 1 ) , d p ( i , 0 ) = d p ( i − 1 , 1 ) + 3 d p ( i − 1 , 0 ) dp(i,1)=2dp(i-1,1),dp(i,0)=dp(i-1,1)+3dp(i-1,0) dp(i,1)=2dp(i1,1),dp(i,0)=dp(i1,1)+3dp(i1,0)
我们不可能对0~3e5每个位置这样dp,太慢了,我就打表看结果有没有规律,发现有…记最后一个包含 x x x的区间下标为 k k k,则贡献为 3 k − 2 ∗ 2 n − k + 1 , k = 1 3^{k-2}*2^{n-k+1},k=1 3k22nk+1,k=1是比较特别的( S 1 S_1 S1没法操作),特判一下.组合含义解释 k k k前面的 o p op op任选的情况下,要么现在含x要么不含,对于两种情况,k都有两种操作使得结果含x,k后面的区间只要不取 ∩ \cap 即可.所以我们用线段树维护包含某一个点的最后一个区间的下标即可.
答案的做法可能更符合一条顺畅的思路,但我线段树造诣不够,没想到.
d p dp dp这个递推很容易想到矩阵快速幂,因为是线性的转移,就是说当加入一个区间时,我们对于该区间包含的点的 d p ( i , 1 ) , d p ( i , 0 ) dp(i,1),dp(i,0) dp(i,1),dp(i,0)作一次矩阵乘法操作即可,就是说线段树维护的是矩阵,乘法也是矩阵,操作完所有区间后,就得到了每一个点的最终dp结果.代码待补…
单点修改,pushup是一个转移矩阵的累乘,用差分的办法,每次只修改左右端点线段树上的值(累乘一个新的转移矩阵)

树长度为n,区间数,从0~3e5遍历数轴,数轴上每一个点的贡献由整颗树的根得来,不同的点对应的树需要修改,但总共修改O(n)次,假设当前处理i,树节点x表示区间x对于i的转移矩阵,(如果区间x包含i则为ONE,否则为ZERO),那么i的贡献就是所有节点值的连乘(dp的过程),就是树的根节点的值,考虑i+1,我们需要记录i+1上的是否是某些区间的右端点+1,是的话就把该区间的树节点置ZERO,是否有某些区间的左端点,是的话就把那些区间的树节点值改为ONE,有点像差分里面的消除影响
单点修改O(logn)+区间查询O(1)
我的是区间修改+单点查询,更慢.

代码

我的

#include<bits/stdc++.h>
using namespace std;
#define pow2(X) (1ll<<(X))
#define SIZE(A) ((int)A.size())
#define LENGTH(A) ((int)A.length())
#define ALL(A) A.begin(),A.end()
#define F(i,a,b) for(ll i=a;i<=(b);++i)
#define dF(i,a,b) for(ll i=a;i>=(b);--i)
#define GETPOS(c,x) (lower_bound(ALL(c),x)-c.begin())
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
#define pb push_back
#define pr pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define eps 1e-6
#define PI acos(-1.0)
#define lb lower_bound
#define ub upper_bound
#define bs binary_search
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
#define SZ 666666
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> ipair;
typedef vector<int> VI;
typedef vector<long long> VLL;
typedef vector<vector<long long > > VVLL;
typedef vector<vector<int> > VVI;
typedef vector<double> VD;
typedef vector<string> VS;
const int mods = 998244353;
//const int mods = 1e9+7;
const int maxn = 3e5+10;
const int N = 5e5+10;
const int E = 1e4+10;
const int lim = 1e6+10;
ll qpow(ll a,ll b) {ll res=1;a%=mods; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mods;a=a*a%mods;}return res;}
ll lcm(ll a, ll b) {return a / __gcd(a, b) * b;}
int read(){ll 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*10+ch-'0';ch=getchar();}return x*f;}

/***********************************
learning time
***********************************/

int n,m;
int l[maxn],r[maxn];
ll maa[maxn<<2],lazy[maxn<<2];
void pushup(int rt) {
	maa[rt] = max(maa[rt << 1],maa[rt << 1 | 1]);
}
//赋值版
void pushdown(int rt) {
	if (lazy[rt]) {
		maa[rt << 1] = lazy[rt];
		maa[rt << 1 | 1] = lazy[rt];
		lazy[rt << 1] = lazy[rt];
		lazy[rt << 1 | 1] = lazy[rt];
		lazy[rt] = 0;
	}
}

void update(int L, int R, int l, int r, int rt,ll C) {
	if (L <= l && r <= R) {
		maa[rt] = C;
		lazy[rt] = C;
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(rt);
	if (L <= mid) update(L, R, l, mid, rt << 1,C);
	if (R > mid) update(L, R, mid + 1, r, rt << 1 | 1,C);
	pushup(rt);
}

ll query_sum(int L, int R, int l, int r, int rt) {
	if (L <= l && r <= R) {
		return maa[rt];
	}
	int mid = (l + r) >> 1;
	pushdown(rt);
	ll ans = 0;
	if (L <= mid) ans = max(ans,query_sum(L,R,l,mid,rt<<1));
	if (R > mid) ans = max(ans,query_sum(L, R, mid + 1, r, rt << 1 | 1));
	return ans;
}

int main(){
	// freopen("C:\\Users\\Gao\\Desktop\\validation_input\\watering_well_chapter_2_input.txt","r",stdin);
	// freopen("C:\\Users\\Gao\\Desktop\\validation_input\\output.txt","w",stdout);
	ios_base::sync_with_stdio(0);

    int T;
    //cin>>T; 
	T = 1;
	F(turn,1,T){
		m = 1;
		cin>>n;
		F(i,1,n){
			cin>>l[i]>>r[i];
			l[i]++;r[i]++;
			m = max(m,r[i]);
		}
		ll ans = 0;

		F(i,1,n){
			int lef = l[i],rig = r[i];
			update(lef,rig,1,m,1,i);
		}
		F(i,1,m){
			ll k = query_sum(i,i,1,m,1);
			//cout<<i<<" "<<k<<endl;
			if(k>1) 
				(ans+=qpow(3,k-2)*qpow(2,n-k+1)%mods)%=mods;
			else if(k==1){
				(ans+=qpow(2,n-k))%=mods;
			}
		}
		cout<<ans<<endl;
	}
}
 
/*

*/

答案

#include<bits/stdc++.h>

using namespace std;

const int N = 300043;

typedef array<int, 2> vec;
typedef array<vec, 2> mat;

const int MOD = 998244353;

mat operator*(const mat& a, const mat& b)
{
    mat c;
    for(int i = 0; i < 2; i++)
        for(int j = 0; j < 2; j++)
            c[i][j] = 0;
    for(int i = 0; i < 2; i++)
        for(int j = 0; j < 2; j++)
            for(int k = 0; k < 2; k++)
                c[i][k] = (a[i][j] * 1ll * b[j][k] + c[i][k]) % MOD;
    return c;
}

mat ZERO = {vec({3, 0}), vec({1, 2})};
mat ONE = {vec({1, 2}), vec({1, 2})};

mat t[4 * N];

void recalc(int v)
{
    t[v] = t[v * 2 + 1] * t[v * 2 + 2];    
}

void build(int v, int l, int r)
{
    if(l == r - 1)
    {            
        t[v] = ZERO;                       
    }
    else
    {
        int m = (l + r) / 2;
        build(v * 2 + 1, l, m);
        build(v * 2 + 2, m, r);
        recalc(v);               
    }
}

void upd(int v, int l, int r, int pos, int val)
{
    if(l == r - 1)
    {
        if(val == 0) t[v] = ZERO;
        else t[v] = ONE;
    }
    else
    {
        int m = (l + r) / 2;
        if(pos < m) upd(v * 2 + 1, l, m, pos, val);
        else upd(v * 2 + 2, m, r, pos, val);
        recalc(v);
    }
}

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin >> n;
    vector<vector<pair<int, int>>> v(N);
    for(int i = 0; i < n; i++)
    {
        int l, r;
        cin >> l >> r;
        v[l].push_back(make_pair(1, i));
        v[r + 1].push_back(make_pair(0, i));
    }    
    build(0, 0, n - 1);
    int cur = 0;
    int ans = 0;
    for(int i = 0; i <= 300000; i++)
    {
        for(auto x : v[i])
        {
            if(x.second == 0) cur = x.first;
            else upd(0, 0, n - 1, x.second - 1, x.first);
        }
        ans = (ans + t[0][cur][1]) % MOD;
    }
    cout << ans << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值