专题突破一之分块——Untitled Problem II,Balanced Lineup,[ioi2009]Regions

本文探讨了三个问题:SP2940的未命名问题I,通过源代码实现的平衡阵容算法,以及在[IOI2009]竞赛中的区域统计。文章涉及区间标记、分块处理和动态规划优化,展示了如何在整块和散块中高效查询和更新数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SP2940 UNTITLE1 - Untitled Problem II

source

solution

分块
s i = { s i + k × ( i − l + 1 ) = s i + k × i + k × ( 1 − l ) l ≤ i ≤ r s i + k × ( r − l + 1 ) r < i s_i=\begin{cases} s_i+k\times (i-l+1)=s_i+k\times i+k\times (1-l)&&l\le i\le r\\ s_i+k\times (r-l+1)&&r<i \end{cases} si={si+k×(il+1)=si+k×i+k×(1l)si+k×(rl+1)lirr<i
对于整块而言 k × ( 1 − l ) k\times(1-l) k×(1l) k × ( r − l + 1 ) k\times (r-l+1) k×(rl+1)都可以看作常数,用区间标记tag记录,再单独记录一下k
a n s = max ⁡ { s i + k b l o c k i × i + t a g b l o c k i } l ≤ i ≤ r ans=\max\bigg\{s_i+k_{block_i}\times i+tag_{block_i}\bigg\}\qquad l\le i\le r ans=max{si+kblocki×i+tagblocki}lir
对于 l , r l,r l,r在的散块,暴力下放标记到块区间内,然后暴力查询

对于整块,发现max的方程类似 d p dp dp​斜率优化转移,维护块内的上凸壳,二分求解

  • 假设块内 i < j i<j i<j,且 a n s i < a n s j ans_i<ans_j ansi<ansj

    • a n s i = s i + k b l o c k p i + t a g b l o c k p ans_i=s_i+k_{block_p}i+tag_{block_p} ansi=si+kblockpi+tagblockp

    • a n s j = s j + k b l o c k p j + t a g b l o c k p ans_j=s_j+k_{block_p}j+tag_{block_p} ansj=sj+kblockpj+tagblockp

  • a n s i < a n s j ⇔ s i + k b l o c k p i < s j + k b l o c k p j ⇔ s i − s j < k b l o c k p ( j − i ) ⇒ s j − s i j − i > − k b l o c k p ans_i<ans_j\Leftrightarrow s_i+k_{block_p}i<s_j+k_{block_p}j\Leftrightarrow s_i-s_j<k_{block_p}(j-i)\\\Rightarrow \frac{s_j-s_i}{j-i}>-k_{block_p} ansi<ansjsi+kblockpi<sj+kblockpjsisj<kblockp(ji)jisjsi>kblockp

code

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 50005
#define maxB 255
int n, B;
int s[maxn], block[maxn];

struct node {
	int l, r, tag, k, top;
	
	struct point {
		int x, y;
		point(){}
		point( int X, int Y ) { x = X, y = Y; }
		friend double slope( point s, point t ) { return 1.0 * ( s.y - t.y ) / ( s.x - t.x ); }
	}sta[maxB];
	
	void build_hall() {
		top = k = tag = 0;
		for( int i = l;i <= r;i ++ ) {
			point now( i, s[i] );
			while( top > 1 && slope( sta[top], now ) >= slope( sta[top - 1], now ) ) top --;
			sta[++ top] = now;
		}
		sta[top + 1] = point( r + 1, -1e18 );
	}
	
	int calc() {
		int l = 1, r = top, pos = 0;
		while( l <= r ) {
			int mid = ( l + r ) >> 1;
			if( slope( sta[mid + 1], sta[mid] ) > -k ) l = mid + 1;
			else r = mid - 1, pos = mid;
		}
		return tag + sta[pos].x * k + sta[pos].y;
	}
	
	void init( int L, int R ) { l = L, r = R, build_hall(); }
	
	void pushdown() { for( int i = l;i <= r;i ++ ) s[i] += tag + k * i; }
	
}t[maxB];

void modify( int l, int r, int k ) {
	for( int i = block[r] + 1;i <= block[n];i ++ )
		t[i].tag += ( r - l + 1 ) * k;
	if( block[l] == block[r] ) {
		t[block[l]].pushdown();
		for( int i = l;i <= r;i ++ )
			s[i] += ( i - l + 1 ) * k;
		for( int i = r + 1;i <= t[block[l]].r;i ++ )
			s[i] += ( r - l + 1 ) * k;
		t[block[l]].build_hall();
	}
	else {
		for( int i = block[l] + 1;i < block[r];i ++ ) 
			t[i].k += k, t[i].tag -= ( l - 1 ) * k;
		t[block[l]].pushdown();
		t[block[r]].pushdown();
		for( int i = l;i <= t[block[l]].r;i ++ )
			s[i] += ( i - l + 1 ) * k;
		for( int i = t[block[r]].l;i < r;i ++ )
			s[i] += ( i - l + 1 ) * k;
		for( int i = r;i <= t[block[r]].r;i ++ ) 
			s[i] += ( r - l + 1 ) * k;
		t[block[l]].build_hall();
		t[block[r]].build_hall();
	}
}

int query( int l, int r ) {
	int ans = -1e18;
	if( block[l] == block[r] ) {
		t[block[l]].pushdown();
		for( int i = l;i <= r;i ++ )
			ans = max( ans, s[i] );
		t[block[l]].build_hall();
	}
	else {
		t[block[l]].pushdown();
		t[block[r]].pushdown();
		for( int i = l;i <= t[block[l]].r;i ++ ) 
			ans = max( ans, s[i] );
		for( int i = t[block[r]].l;i <= r;i ++ ) 
			ans = max( ans, s[i] );
		for( int i = block[l] + 1;i < block[r];i ++ ) 
			ans = max( ans, t[i].calc() );
		t[block[l]].build_hall();
		t[block[r]].build_hall();
	}
	return ans;
}

signed main() {
	scanf( "%lld", &n );
	B = sqrt( n );
	for( int i = 1;i <= n;i ++ ) {
		scanf( "%lld", &s[i] );
		s[i] += s[i - 1];
		block[i] = ( i - 1 ) / B + 1;
	}
	for( int i = 1;i <= block[n];i ++ )
		t[i].init( B * ( i - 1 ) + 1, min( n, B * i ) );
	int Q, opt, x, y, k;
	scanf( "%lld", &Q );
	while( Q -- ) {
		scanf( "%lld %lld %lld", &opt, &x, &y );
		if( opt ) printf( "%lld\n", query( x, y ) );
		else scanf( "%lld", &k ), modify( x, y, k );
	}
	return 0;
}

Balanced Lineup

source

code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 50005
#define maxB 250
int n, Q, B;
int block[maxn], h[maxn], Max[maxB], Min[maxB];

void solve( int l, int r ) {
	int maxx = -inf, minn = inf;
	for( int i = l;i <= min( block[l] * B, r );i ++ )
		maxx = max( maxx, h[i] ), minn = min( minn, h[i] );
	for( int i = block[l] + 1;i < block[r];i ++ )
		maxx = max( maxx, Max[i] ), minn = min( minn, Min[i] );
	for( int i = max( l, ( block[r] - 1 ) * B + 1 );i <= r;i ++ )
		maxx = max( maxx, h[i] ), minn = min( minn, h[i] );
	printf( "%d\n", maxx - minn );
}

int main() {
	scanf( "%d %d", &n, &Q );
	B = sqrt( n );
	for( int i = 1;i <= n;i ++ ) {
		scanf( "%d", &h[i] );
		block[i] = ( i - 1 ) / B + 1;
	}
	memset( Min, 0x3f, sizeof( Min ) );
	for( int i = 1;i <= n;i ++ ) {
		Max[block[i]] = max( Max[block[i]], h[i] );
		Min[block[i]] = min( Min[block[i]], h[i] );
	}
	while( Q -- ) {
		int l, r;
		scanf( "%d %d", &l, &r );
		solve( l, r );
	}
	return 0;
}

Count on a tree II

已经合并到莫队算法里面去了

[ioi2009]Regions

source
将询问按颜色出现次数分块

小于等于 n \sqrt n n 的就挂在 e 2 e2 e2上,统计从根到该点颜色的出现次数

大于 n \sqrt n n 的就挂在 e 1 e1 e1上,统计该点子树内颜色的出现次数

复杂度就是 n n n\sqrt n nn

#include <map>
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 200005
#define maxR 25005
struct node {
	int x, y;
	node(){}
	node( int X, int Y ) { x = X, y = Y; }
	bool operator < ( node t ) const {
		return x == t.x ? y < t.y : x < t.x;
	}
	bool operator == ( node t ) {
		return x == t.x && y == t.y;
	}
}MS[maxn];
map < node, int > mp;
vector < int > G[maxn], f[maxR], g[maxR];
int n, R, Q, block;
int r[maxn], cnt[maxR], ans[maxn], id[maxn];

void dfs1( int u ) {
	cnt[r[u]] ++;
	for( auto v : g[r[u]] ) ans[v] += cnt[MS[v].x];
	for( auto v : G[u] ) dfs1( v );
	cnt[r[u]] --;
}

void dfs2( int u ) {	
	for( auto v : f[r[u]] ) ans[v] -= cnt[MS[v].y];
	cnt[r[u]] ++;
	for( auto v : G[u] ) dfs2( v );
	for( auto v : f[r[u]] ) ans[v] += cnt[MS[v].y];
}

int main() {
	scanf( "%d %d %d %d", &n, &R, &Q, &r[1] );
	for( int i = 2, x;i <= n;i ++ ) {
		scanf( "%d %d", &x, &r[i] );
		G[x].push_back( i );
	}
	for( int i = 1;i <= n;i ++ )
		cnt[r[i]] ++;
	block = sqrt( n );
	for( int i = 1, x, y;i <= Q;i ++ ) {
		scanf( "%d %d", &x, &y );
		MS[i] = node( x, y );
		if( mp.count( node( x, y ) ) ) id[i] = mp[node( x, y )];
		else {
			id[i] = mp[node( x, y )] = i;
			if( cnt[y] <= block ) g[y].push_back( i );
			else f[x].push_back( i );
		}
	}
	memset( cnt, 0, sizeof( cnt ) );
	dfs1( 1 );
	dfs2( 1 );
	for( int i = 1;i <= Q;i ++ )
		printf( "%d\n", ans[id[i]] );
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值