文章目录
- Weak in the Middle
- source
- solution
- code
- Cutting Plants
- source
- solution
- code
- [HNOI2016]最小公倍数
- source
- solution
- code
Weak in the Middle
source
solution
栈模拟。
天数的计算,可以发现与其参与三元比较的次数有关
对于栈顶,如果被弹出,那么天数就是max(\max(max(栈顶的参与次数,栈中栈顶的下一个的参与次数)+1)+1)+1
code
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 100005
int T, n;
int sta[maxn], cnt[maxn], ans[maxn], x[maxn];int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d", &n );for( int i = 1;i <= n;i ++ ) {scanf( "%d", &x[i] );ans[i] = cnt[i] = 0;}int top = 0;for( int i = 1;i <= n;i ++ ) {while( top > 1 && x[sta[top]] < x[sta[top - 1]] && x[sta[top]] < x[i] ) {ans[sta[top]] = max( cnt[sta[top - 1]], cnt[sta[top]] ) + 1;cnt[sta[top - 1]] = ans[sta[top]];top --;}sta[++ top] = i;}for( int i = 1;i <= n;i ++ )printf( "%d ", ans[i] );}return 0;
}
Cutting Plants
source
solution
考场上调一个取反符号调完正常,非常好
非常标准的分块可做 只不过实现。。。
首先,先判无解,必须每棵树的目标高度要小于等于高度
把nnn棵树分成n\sqrt nn块
O(n)O(n)O(n)线性枚举iii,强制要求该操作后至少第iii个都必须达到目标要求
相当于固定操作的区间左端点iii,贪心的,想尽可能右移区间右端点
-
先从iii开始暴力扫一遍iii所在的块,并把标记tagtagtag下放
-
符合要求:所有树的目标高度都小于等于iii的目标高度 且 所有树的当前高度都大于等于iii目标高度,这样才能操作
-
从下一块开始整块枚举
为了直接判断整块是否符合要求
用maxxmaxxmaxx记录块中所有树的目标高度的最大值
hhh记录块中所有的现在高度的最小值
满足bi≥maxxj,bi≤hjb_i\ge maxx_j,b_i\le h_jbi≥maxxj,bi≤hj
-
满足要求
更新整块的信息,所有树的当前高度都变成bib_ibi,用tagtagtag记录整块被操作后的同一高度
-
否则,在当前块
break
再从当前块头开始暴力遍历该块,tagtagtag下放,可能当前块的前一部分是满足操作条件的,暴力操作更新
-
-
-
不符合要求,更新到符合要求的最后一棵树就
return
更新:将jjj树的现在高度,直接变成iii的目标高度;更新块的所有树的现在高度的最小值
-
code
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 100005
#define maxB 320
int T, n, B;
int a[maxn], b[maxn], block[maxn], maxx[maxB], tag[maxB], h[maxB];void modify( int pos ) {int End = 0;if( tag[block[pos]] ) {for( int i = pos;i <= min( n, B * block[pos] );i ++ )a[i] = tag[block[pos]];tag[block[pos]] = 0;}for( int i = pos;i <= min( n, B * block[pos] );i ++ )if( a[i] < b[pos] || b[pos] < b[i] ) { End = i; break; }else a[i] = b[pos], h[block[pos]] = min( h[block[pos]], a[i] );if( End ) {h[block[pos]] = 2e9, maxx[block[pos]] = 0;for( int i = End;i <= min( n, B * block[pos] );i ++ ) {h[block[pos]] = min( h[block[pos]], a[i] );maxx[block[pos]] = max( maxx[block[pos]], b[i] );}return;}End = 0;for( int i = block[pos] + 1;i <= block[n];i ++ )if( h[i] < b[pos] || b[pos] < maxx[i] ) { End = i; break; }else tag[i] = h[i] = b[pos];if( ! End ) return;if( tag[End] ) {for( int i = ( End - 1 ) * B + 1;i <= min( n, End * B );i ++ )a[i] = tag[End];tag[End] = 0;}for( int i = ( End - 1 ) * B + 1;i <= min( n, End * B );i ++ )if( a[i] < b[pos] || b[pos] < b[i] ) break;else a[i] = b[pos], h[End] = min( h[End], a[i] );
}int main() {scanf( "%d", &T );next :while( T -- ) {scanf( "%d", &n );B = sqrt( n );for( int i = 1;i <= n;i ++ ) {scanf( "%d", &a[i] );block[i] = ( i - 1 ) / B + 1;}for( int i = 1;i <= n;i ++ )scanf( "%d", &b[i] );for( int i = 1;i <= n;i ++ )if( a[i] < b[i] ) { printf( "-1\n" ); goto next; }for( int i = 1;i <= block[n];i ++ ) h[i] = 2e9, maxx[i] = tag[i] = 0;for( int i = 1;i <= n;i ++ ) {maxx[block[i]] = max( maxx[block[i]], b[i] );h[block[i]] = min( h[block[i]], a[i] );}int ans = 0;for( int i = 1;i <= n;i ++ )if( a[i] == b[i] || b[i] == tag[block[i]] ) continue;else modify( i ), ans ++;printf( "%d\n", ans );}return 0;
}
[HNOI2016]最小公倍数
source
solution
分块。
答案为yes
说明从uuu到vvv有一条路径上的aaa的最大值和bbb的最大值恰好等于询问给定的a,ba,ba,b
先将所有边按aaa从小到大排序,然后将所有询问按bbb从小到大排序
将边分块,把所有询问挂在 最大的 满足小于等于询问的aaa 的边 所在的块
- 具体而言就是枚举块iii,然后枚举询问,把aaa大于等于这个块的块头的aaa,且小于下个块块头的aaa的询问拎出来
将这个块前面的所有块的边重新按bbb排序
枚举拎出来的询问(因为最开始的操作,所以按bbb递增)
将满足bbb小于等于询问的bbb的边加入并查集合并(先前的操作保证aaa一定小于等于询问的aaa)
然后暴力枚举本块中满足的边,并查集合并(判断a,ba,ba,b都满足才行)
更新枚举的询问的答案,要判断a,ba,ba,b和最大值相等以外,还要保证两点u,vu,vu,v连通(父亲相同)
最后把暴力枚举的边,回退掉
所以是可撤销并查集,不能路径压缩
块的大小为nlogm\sqrt{n\log m}nlogm
code
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 100005
struct node {int u, v, a, b, id;node(){}node( int U, int V, int A, int B, int ID = 0 ) {u = U, v = V, a = A, b = B, id = ID;}
}g[maxn], E[maxn], Q[maxn], MS[maxn];
int n, m, q, top;
int f[maxn], siz[maxn], maxA[maxn], maxB[maxn], ans[maxn];int find( int x ) {return x == f[x] ? x : find( f[x] );
}void merge( int u, int v, int a, int b ) {u = find( u ), v = find( v );if( siz[u] < siz[v] ) swap( u, v );MS[++ top] = node( u, v, maxA[u], maxB[u], siz[u] );if( u ^ v ) {f[v] = u;siz[u] += siz[v];maxA[u] = max( maxA[u], maxA[v] );maxB[u] = max( maxB[u], maxB[v] );}maxA[u] = max( maxA[u], a );maxB[u] = max( maxB[u], b );
}int main() {scanf( "%d %d", &n, &m );int block = sqrt( m * log2( n ) );for( int i = 1, u, v, a, b;i <= m;i ++ ) {scanf( "%d %d %d %d", &u, &v, &a, &b );E[i] = node( u, v, a, b );}sort( E + 1, E + m + 1, []( node x, node y ) { return x.a == y.a ? x.b < y.b : x.a < y.a; } );scanf( "%d", &q );for( int i = 1, u, v, a, b;i <= q;i ++ ) {scanf( "%d %d %d %d", &u, &v, &a, &b );Q[i] = node( u, v, a, b, i );}sort( Q + 1, Q + q + 1, []( node x, node y ) { return x.b == y.b ? x.a < y.a : x.b < y.b; } );for( int k = 1;k <= m;k += block ) {for( int i = 1;i <= n;i ++ )f[i] = i, siz[i] = 1, maxA[i] = maxB[i] = -1;int cnt = 0;for( int i = 1;i <= q;i ++ )if( E[k].a <= Q[i].a && ( k + block > m || Q[i].a < E[k + block].a ) )g[++ cnt] = Q[i];if( ! cnt ) continue;else sort( E + 1, E + k, []( node x, node y ) { return x.b == y.b ? x.a < y.a : x.b < y.b; } );for( int i = 1, j = 1;i <= cnt;i ++ ) {for( ;j < k && E[j].b <= g[i].b;j ++ )merge( E[j].u, E[j].v, E[j].a, E[j].b );top = 0;for( int w = k;w <= min( m, k + block - 1 );w ++ )if( E[w].a <= g[i].a && E[w].b <= g[i].b )merge( E[w].u, E[w].v, E[w].a, E[w].b );int u = find( g[i].u ), v = find( g[i].v );ans[g[i].id] = ( u == v && maxA[u] == g[i].a && maxB[u] == g[i].b );for( int w = top;w;w -- ) {u = MS[w].u;v = MS[w].v;f[v] = v;maxA[u] = MS[w].a;maxB[u] = MS[w].b;siz[u] = MS[w].id;}top = 0;}}for( int i = 1;i <= q;i ++ )if( ans[i] ) printf( "Yes\n" );else printf( "No\n" );return 0;
}