UOJ#291. 【ZJOI2017】树状数组

链接:http://uoj.ac/problem/291

二维数点

首先推一推性质大概可以发现这是单点修改,求后缀和

(其实也可以理解为区间(前缀)修改,单点查询)

然后再简单推一推就可以知道如果i被修改了a[i]次,总共修改了sum次

如果l=1,算错的情况就是sum ^ a[r] = 1

否则就是a[l - 1] ^ a[r] = 1

然后如果只是单点查询,我们就可以直接线段树

但是如果l-1,r都被某个区间覆盖,它们并不会同时被修改

所以我们要分类讨论,就变成了二维数点问题,一维左端点,一维右端点

然后树套树

#include <bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define fill( x, y ) memset( x, y, sizeof x )
#define copy( x, y ) memcpy( x, y, sizeof x )
using namespace std;

typedef long long LL;
typedef pair < int, int > pa;

inline int read()
{
	int sc = 0; char ch = getchar();
	while( ch < '0' || ch > '9' ) ch = getchar();
	while( ch >= '0' && ch <= '9' ) sc = sc * 10 + ch - '0', ch = getchar();
	return sc;
}

const int MAXN = 400010;
const int MAXM = 70000000;
const int mod = 998244353;

int n, rt[MAXN], f[MAXM], ls[MAXM], rs[MAXM], cur, Q, tot;

inline int qpow(int x, int y) { int ret = 1; for( ; y ; y >>= 1, x = 1LL * x * x % mod ) if( y & 1 ) ret = 1LL * ret * x % mod; return ret; }

inline int merge(int a, int b) { return ( 1LL * a * ( mod + 1 - b ) + 1LL * b * ( mod + 1 - a ) ) % mod; }

inline void add2(int &x, int l, int r, int ql, int qr, int v)
{
	if( !x ) x = ++tot;
	if( l == ql && r == qr ) { f[ x ] = merge( f[ x ], v ); return ; }
	int mid = l + r >> 1;
	if( qr <= mid ) add2( ls[ x ], l, mid, ql, qr, v );
	else if( ql > mid ) add2( rs[ x ], mid + 1, r, ql, qr, v );
	else add2( ls[ x ], l, mid, ql, mid, v ), add2( rs[ x ], mid + 1, r, mid + 1, qr, v );
}

inline void add1(int x, int l, int r, int ql, int qr, int nxtl, int nxtr, int v)
{
	if( l == ql && r == qr ) { add2( rt[ x ], 0, n, nxtl, nxtr, v ); return ; }
	int mid = l + r >> 1;
	if( qr <= mid ) add1( x << 1, l, mid, ql, qr, nxtl, nxtr, v );
	else if( ql > mid ) add1( x << 1 | 1, mid + 1, r, ql, qr, nxtl, nxtr, v );
	else add1( x << 1, l, mid, ql, mid, nxtl, nxtr, v ), add1( x << 1 | 1, mid + 1, r, mid + 1, qr, nxtl, nxtr, v );
}

inline int query2(int x, int l, int r, int p)
{
	if( !x ) return 0;
	int ret = f[ x ];
	if( l == r ) return ret;
	int mid = l + r >> 1;
	if( p <= mid ) return merge( query2( ls[ x ], l, mid, p ), ret );
	return merge( query2( rs[ x ], mid + 1, r, p ), ret );
}

inline int query1(int x, int l, int r, int p, int v)
{
	int ret = query2( rt[ x ], 0, n, v );
	if( l == r ) return ret;
	int mid = l + r >> 1;
	if( p <= mid ) return merge( query1( x << 1, l, mid, p, v ), ret );
	return merge( query1( x << 1 | 1, mid + 1, r, p, v ), ret );
}

int main()
{
#ifdef wxh010910
	freopen( "data.in", "r", stdin );
#endif
	n = read() + 1; Q = read();
	while( Q-- )
	{
		int opt = read(), l = read(), r = read(), len = r - l + 1, inv = qpow( len, mod - 2 );
		if( opt == 1 )
		{
			add1( 1, 0, n, l, r, l, r, 1LL * 2 * inv % mod );
			add1( 1, 0, n, 0, l - 1, l, r, inv );
			add1( 1, 0, n, l, r, r + 1, n, inv );
			cur++;
		}
		else
		{
			if( l == 1 && ( cur & 1 ) ) printf( "%d\n", query1( 1, 0, n, 0, r ) );
			else printf( "%d\n", ( mod + 1 - query1( 1, 0, n, l - 1, r ) ) % mod );
		}
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值