文章目录
Chef and Churu
source
solution
对于单独的 i i i,查询可以用线段树/树状数组 O ( n log n ) O(n\log n) O(nlogn),这暗示可以平衡查询修改次数
分块
预处理 c n t i , j : cnt_{i,j}: cnti,j: A j A_j Aj对第 i i i块包含的函数贡献次数
每次修改的时候,相当于 + v a l − A p o s +val-A_{pos} +val−Apos
枚举块,直接整体修改 c n t × ( v a l − A p o s ) cnt\times(val-A_{pos}) cnt×(val−Apos)
- 如果是 O ( log n ) O(\log n) O(logn)修改值
- 查询整块直接调用 n \sqrt{n} n
- 散块区间查询 n log n \sqrt{n}\log n nlogn
- 最后时间复杂度是 O ( Q 1 ( n + l o g n ) + Q 2 ( n + n log n ) ) O(Q_1(\sqrt{n}+logn)+Q_2(\sqrt n+\sqrt n\log n)) O(Q1(n+logn)+Q2(n+nlogn))
- 卡在查询的 n log n \sqrt{n}\log n nlogn,非常难受
再分块, O ( n ) O(\sqrt{n}) O(n) 修改, O ( 1 ) O(1) O(1)查询
用tag
记录整块的整体加标记,w
记录散块暴力加
查询的时候,找到
i
i
i的w
值再加上所在块的整体加tag
时间复杂度 O ( Q 1 ( n + n ) + Q 2 ( n + n ) ) = O ( Q n ) O(Q_1(\sqrt{n}+\sqrt{n})+Q_2(\sqrt n+\sqrt n))=O(Q\sqrt n) O(Q1(n+n)+Q2(n+n))=O(Qn)
code
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
#define int unsigned long long
#define maxn 100005
#define maxB 320
int n, Q, B;
int A[maxn], L[maxn], R[maxn], block[maxn], sum[maxB], w[maxn], tag[maxB];
int cnt[maxB][maxn];
void modify( int pos, int val ) {
for( int i = 1;i <= block[n];i ++ )
sum[i] += cnt[i][pos] * ( val - A[pos] );
for( int i = block[pos] + 1;i <= block[n];i ++ )
tag[i] += val - A[pos];
for( int i = pos;i <= min( n, B * block[pos] );i ++ )
w[i] += val - A[pos];
A[pos] = val;
}
int query( int i ) { return w[i] + tag[block[i]]; }
int query( int l, int r ) {
int ans = 0;
if( block[l] == block[r] )
for( int i = l;i <= r;i ++ )
ans += query( R[i] ) - query( L[i] - 1 );
else {
for( int i = l;i <= B * block[l];i ++ )
ans += query( R[i] ) - query( L[i] - 1 );
for( int i = B * ( block[r] - 1 ) + 1;i <= r;i ++ )
ans += query( R[i] ) - query( L[i] - 1 );
for( int i = block[l] + 1;i < block[r];i ++ )
ans += sum[i];
}
return ans;
}
signed main() {
scanf( "%llu", &n );
B = sqrt( n );
for( int i = 1;i <= n;i ++ ) {
scanf( "%llu", &A[i] ), w[i] = A[i];
block[i] = ( i - 1 ) / B + 1;
}
for( int i = 1;i <= n;i ++ )
scanf( "%llu %llu", &L[i], &R[i] );
for( int i = 1;i <= n;i ++ )
w[i] += w[i - 1];
for( int i = 1;i <= block[n];i ++ ) {
for( int j = ( i - 1 ) * B + 1;j <= min( n, i * B );j ++ )
cnt[i][L[j]] ++, cnt[i][R[j] + 1] --;
for( int j = 1;j <= n;j ++ )
cnt[i][j] += cnt[i][j - 1];
for( int j = 1;j <= n;j ++ )
sum[i] += cnt[i][j] * A[j];
}
scanf( "%llu", &Q );
int opt, x, y;
while( Q -- ) {
scanf( "%llu %llu %llu", &opt, &x, &y );
if( opt & 1 ) modify( x, y );
else printf( "%llu\n", query( x, y ) );
}
return 0;
}
Chef and Problems
source
solution
分块
预处理出整块 i , j i,j i,j之间的答案
具体而言,用 l a s t i last_i lasti记录年龄为 i i i的第一个人的出现位置,显然越往后的人与第一个年龄相同的人距离越大
对于查询,包含的整块直接调用预处理的数组
散块部分,就在区间中lower_bound
找最远的,把人按年龄分道不同容器vector
找坐标
code
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define maxn 100005
#define maxB 1005
vector < int > pos[maxn];
int n, m, Q, B;
int A[maxn], block[maxn], last[maxn];
int len[maxB][maxB];
int main() {
scanf( "%d %d %d", &n, &m, &Q );
B = 100;
for( int i = 1;i <= n;i ++ ) {
scanf( "%d", &A[i] );
block[i] = ( i - 1 ) / B + 1;
}
for( int i = 1;i <= block[n];i ++ ) {
for( int j = 1;j <= m;j ++ ) last[j] = 0;
int l = ( i - 1 ) * B + 1, ans = 0;
for( int j = l;j <= n;j ++ ) {
if( ! last[A[j]] ) last[A[j]] = j;
else ans = max( ans, j - last[A[j]] );
if( j % B == 0 ) len[i][block[j]] = ans;
}
}
for( int i = 1;i <= n;i ++ ) pos[A[i]].push_back( i );
while( Q -- ) {
int l, r;
scanf( "%d %d", &l, &r );
int ans = len[block[l] + 1][block[r] - 1];
for( int i = l;i <= min( r, block[l] * B );i ++ ) {
int p = lower_bound( pos[A[i]].begin(), pos[A[i]].end(), r ) - pos[A[i]].begin() - 1;
if( ! ~ p ) continue;
else ans = max( ans, pos[A[i]][p] - i );
}
for( int i = max( l, ( block[r] - 1 ) * B + 1 );i <= r;i ++ ) {
int p = lower_bound( pos[A[i]].begin(), pos[A[i]].end(), l ) - pos[A[i]].begin();
if( p == pos[A[i]].size() ) continue;
else ans = max( ans, i - pos[A[i]][p] );
}
printf( "%d\n", ans );
}
return 0;
}
Children Trips
source
solution
先dfs
确定每个点到根的距离
d
i
s
dis
dis以及深度
d
e
p
dep
dep
按 c c c与 n \sqrt{n} n的大小关系分块
-
c > n c>\sqrt n c>n
对于 u , v u,v u,v,找到其 l c a lca lca,暴力一次一次跳,一次最多跳 p p p长度,最多跳 n \sqrt n n次
最后得到跳的次数以及距离 l c a lca lca的距离,两个距离合在一起看是再跳一次还是两次
-
c ≤ n c\le \sqrt n c≤n
这个时候就不能暴力跳了,因为可能会达到 n n n级别
但是 c c c最多只有 n \sqrt n n个取值
将排序按 c c c从小到大排序,预处理倍增数组 g i , j : g_{i,j}: gi,j: 点 i i i跳 2 j 2^j 2j次所达到的点
按理来说 O ( n n log n ) O(n\sqrt n \log n) O(nnlogn)有点尴尬,但这道题是八秒,交上去倒是挺快的,╮(╯▽╰)╭
code
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define Pair pair < int, int >
#define maxn 100005
int n, m, B;
vector < Pair > G[maxn];
int dis[maxn], dep[maxn], ans[maxn];
int f[maxn][20], g[maxn][20];
void dfs( int u, int fa ) {
f[u][0] = fa, dep[u] = dep[fa] + 1;
for( int i = 1;i <= 16;i ++ )
f[u][i] = f[f[u][i - 1]][i - 1];
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first;
int w = G[u][i].second;
if( v == fa ) continue;
else dis[v] = dis[u] + w, dfs( v, u );
}
}
struct node {
int u, v, p, id;
node(){}
node( int U, int V, int P, int ID ) { u = U, v = V, p = P, id = ID; }
}block_little[maxn], block_large[maxn];
int get_lca( int u, int v ) {
if( dep[u] < dep[v] ) swap( u, v );
for( int i = 16;~ i;i -- )
if( dep[f[u][i]] >= dep[v] )
u = f[u][i];
if( u == v ) return u;
for( int i = 16;~ i;i -- )
if( f[u][i] ^ f[v][i] )
u = f[u][i], v = f[v][i];
return f[u][0];
}
int jump_once( int u, int p ) {
int t = u;
for( int i = 16;~ i;i -- ) {
if( dis[u] - dis[f[t][i]] > p ) continue;
else t = f[t][i];
}
return t;
}
Pair climb_large( int u, int lca, int p ) {
int cnt = 0;
while( u ^ lca ) {
int t = jump_once( u, p );
if( dep[t] > dep[lca] ) cnt ++, u = t;
else break;
}
return make_pair( cnt, dis[u] - dis[lca] );
}
void solve_large( int n ) {
for( int i = 1;i <= n;i ++ ) {
int u = block_large[i].u;
int v = block_large[i].v;
int p = block_large[i].p;
int id = block_large[i].id;
int lca = get_lca( u, v );
Pair l = climb_large( u, lca, p );
Pair r = climb_large( v, lca, p );
int len = l.second + r.second;
ans[id] = l.first + r.first + ( int )ceil( len * 1.0 / p );
}
}
Pair climb_little( int u, int lca, int p ) {
int cnt = 0;
for( int i = 16;~ i;i -- )
if( dep[g[u][i]] > dep[lca] )
cnt += 1 << i, u = g[u][i];
return make_pair( cnt, dis[u] - dis[lca] );
}
void build( int p ) {
for( int i = 1;i <= n;i ++ ) g[i][0] = jump_once( i, p );
for( int j = 1;j <= 16;j ++ )
for( int i = 1;i <= n;i ++ )
g[i][j] = g[g[i][j - 1]][j - 1];
}
void solve_little( int n ) {
sort( block_little + 1, block_little + n + 1, []( node x, node y ) { return x.p < y.p; } );
for( int i = 1;i <= n;i ++ ) {
int u = block_little[i].u;
int v = block_little[i].v;
int p = block_little[i].p;
int id = block_little[i].id;
int lca = get_lca( u, v );
if( p != block_little[i - 1].p ) build( p );
Pair l = climb_little( u, lca, p );
Pair r = climb_little( v, lca, p );
int len = l.second + r.second;
ans[id] = l.first + r.first + ( int )ceil( len * 1.0 / p );
}
}
int main() {
scanf( "%d", &n );
for( int i = 1, u, v, w;i < n;i ++ ) {
scanf( "%d %d %d", &u, &v, &w );
G[u].push_back( make_pair( v, w ) );
G[v].push_back( make_pair( u, w ) );
}
dfs( 1, 0 );
B = sqrt( n );
scanf( "%d", &m );
int little = 0, large = 0;
for( int i = 1, u, v, p;i <= m;i ++ ) {
scanf( "%d %d %d", &u, &v, &p );
if( p <= B ) block_little[++ little] = node( u, v, p, i );
else block_large[++ large] = node( u, v, p, i );
}
solve_large( large );
solve_little( little );
for( int i = 1;i <= m;i ++ ) printf( "%d\n", ans[i] );
return 0;
}