题目

  比赛界面

T1

  比较简单。容易想到是求鱼竿的最大独立集。由于题目的鱼竿可以被分割为二分图,就可以想到最大匹配。

  尝试建边之后会发现边的数量不小,但联系题目性质会发现对于一条鱼竿,它会影响的垂直方向上的鱼竿一定是一个区间,因此再套一发线段树优化即可。

  这里不建议写倍增优化,因为倍增的点是\(O(n\log_2n)\),而网络流的时间是\(O(n^2m)\),因此可能会慢一些。

  当然,你知道,这细网咯流咩......时间复杂度还是比较emmmm......的

  代码:

#include <cstdio>

const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 5, MAXE = 1e6 + 5; template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
} template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
} template<typename _T>
_T MAX( const _T a, const _T b )
{
return a > b ? a : b;
} template<typename _T>
_T MIN( const _T a, const _T b )
{
return a < b ? a : b;
} struct edge
{
int to, nxt, w;
}Graph[MAXN << 1]; const int S = 1, T = 2; int ID[MAXN], q[MAXN];
int a[MAXN], b[MAXN], c[MAXN], d[MAXN];
int head[MAXN], dep[MAXN], cur[MAXN];
int N, cnt = 1, siz = 2; void addEdge( const int from, const int to, const int W )
{
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from], Graph[cnt].w = W;
head[from] = cnt;
} void addE( const int from, const int to, const int W )
{
addEdge( from, to, W ), addEdge( to, from, 0 );
} bool build( const int u, const int l, const int r )
{
if( l > r ) return false;
ID[u] = ++ siz;
if( l == r ) { addE( siz, T, 1 ); return true; }
int mid = l + r >> 1;
if( build( u << 1, l, mid ) ) addE( ID[u], ID[u << 1], INF );
if( build( u << 1 | 1, mid + 1, r ) ) addE( ID[u], ID[u << 1 | 1], INF );
return true;
} void update( const int u, const int l, const int r, const int segL, const int segR, const int sta )
{
if( segL > segR ) return ;
if( segL <= l && r <= segR ) { addE( sta, ID[u], INF ); return ; }
int mid = l + r >> 1;
if( segL <= mid ) update( u << 1, l, mid, segL, segR, sta );
if( mid < segR ) update( u << 1 | 1, mid + 1, r, segL, segR, sta );
} bool BFS( const int Sta, const int Tar )
{
int h = 1, t = 0, u;
for( int i = 1 ; i <= siz ; i ++ ) dep[i] = INF;
dep[q[++ t] = Sta] = 0;
while( h <= t )
{
u = q[h ++];
for( int i = head[u], v ; i ; i = Graph[i].nxt )
if( Graph[i].w && dep[v = Graph[i].to] == INF )
dep[q[++ t] = v] = dep[u] + 1;
}
return dep[Tar] < INF;
} int DFS( const int u, const int lin, const int T )
{
if( u == T ) return lin;
int v, w, used = 0, res = 0;
for( int &i = cur[u] ; i ; i = Graph[i].nxt )
{
v = Graph[i].to, w = Graph[i].w;
if( dep[v] == dep[u] + 1 && w && ( res = DFS( v, MIN( lin - used, w ), T ) ) )
{
used += res, Graph[i].w -= res, Graph[i ^ 1].w += res;
if( used == lin ) break;
}
}
if( used < lin ) dep[u] = INF;
return used;
} int Dinic()
{
int ret = 0;
while( BFS( S, T ) )
{
for( int i = 1 ; i <= siz ; i ++ ) cur[i] = head[i];
ret += DFS( S, INF, T );
}
return ret;
} int main()
{
freopen( "B.in", "r", stdin );
freopen( "B.out", "w", stdout );
read( N );
for( int i = 1 ; i <= N ; i ++ ) read( a[i] );
for( int i = 1 ; i <= N ; i ++ ) read( b[i] );
for( int i = 1 ; i <= N ; i ++ ) read( c[i] );
for( int i = 1 ; i <= N ; i ++ ) read( d[i] ); for( int i = 1 ; i <= N << 1 ; i ++ ) addE( S, ++ siz, 1 );
//Up and Down
int lmost, rmost;
build( 1, 1, N << 1 );
for( int i = 1 ; i <= N ; i ++ )
{
lmost = INF, rmost = -INF;
for( int j = 1 ; j <= a[i] ; j ++ ) if( c[j] >= i ) { lmost = j; break; }
for( int j = a[i] ; j ; j -- ) if( c[j] >= i ) { rmost = j; break; }
update( 1, 1, N << 1, lmost, rmost, i + 2 ); lmost = INF, rmost = -INF;
for( int j = 1 ; j <= a[i] ; j ++ ) if( d[j] > N - i ) { lmost = j; break; }
for( int j = a[i] ; j ; j -- ) if( d[j] > N - i ) { rmost = j; break; }
update( 1, 1, N << 1, lmost + N, rmost + N, i + 2 ); lmost = INF, rmost = -INF;
for( int j = N - b[i] + 1 ; j <= N ; j ++ ) if( c[j] >= i ) { lmost = j; break; }
for( int j = N ; j > N - b[i] ; j -- ) if( c[j] >= i ) { rmost = j; break; }
update( 1, 1, N << 1, lmost, rmost, i + N + 2 ); lmost = INF, rmost = -INF;
for( int j = N - b[i] + 1 ; j <= N ; j ++ ) if( d[j] > N - i ) { lmost = j; break; }
for( int j = N ; j > N - b[i] ; j -- ) if( d[j] > N - i ) { rmost = j; break; }
update( 1, 1, N << 1, lmost + N, rmost + N, i + N + 2 );
}
write( 4 * N - Dinic() ), putchar( '\n' );
return 0;
}

T2

  非常比较难受的题目。

  考虑\(5\sim7\)的部分分,可以想到一个做法:设\(suf(i)\)为\(\min\{j|j\in(i,n],a_j=a_i+1\}\)(不存在则为\(n+1\))。那么我们求解的过程实际上就是在询问区间的范围内,尝试在\(suf\)上走最远。

  由于没有修改,因此\(suf\)也不会变,我们就可以倍增一发,然后询问的复杂度就降下来了。

  时间复杂度\(O(n\log_2n)-O(q\log_2n)\)。

  考虑把上面的做法扩展到可修改。不难发现,如果从\(i\)向\(suf(i)\)连边,那么我们可以得到一棵树。于是我们就想到了使用 LCT 。

  不过,还有问题是,如果树变成了菊花图(比如\(1,1,1,1,...,2\))的情况,那么对根的修改的复杂度将会大得无法接受。

  对于这个问题,我们重新构树——\(suf(i)\)变成\(\min\{j|j\in(i,n],a_j=a_i+1\lor a_j=a_i\}\)。这样的话,树最多是二叉的,修改的时间就可降下来。我们对于\(a_i=a_{suf(i)}\)的点,将它点权赋 0 ,对于另一类点赋 1 ,这样链上的点权和就可以对应到询问答案。

  用 set 维护每种颜色的点的出现位置。修改的时候,把所有会被影响的点都拉出来改一遍;查询的时候,先找到区间里面第一个\(k\)的位置\(p\),再在 LCT 上找出\([l,r]\)区间中最后一个在\(p\)到根的链上的点,然后求解。

  时间\(O(n\log_2n)-O(q\log_2n)\)。

  代码:

#include <set>
#include <cstdio>
using namespace std; typedef set<int> :: iterator iter; const int MAXN = 1e6 + 5; template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
} template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
} template<typename _T>
void swapp( _T &x, _T &y )
{
_T t = x; x = y, y = t;
} template<typename _T>
_T MIN( const _T a, const _T b )
{
return a < b ? a : b;
} template<typename _T>
_T MAX( const _T a, const _T b )
{
return a > b ? a : b;
} set<int> pos[MAXN]; int ch[MAXN][2], fa[MAXN], val[MAXN], su[MAXN];
int a[MAXN], nxt[MAXN];
int N, Q;
bool rev[MAXN]; bool chk( const int x ) { return ch[fa[x]][1] == x; }
void upt( const int x ) { su[x] = su[ch[x][0]] + su[ch[x][1]] + val[x]; }
bool isRt( const int x ) { return ch[fa[x]][0] ^ x && ch[fa[x]][1] ^ x; }
bool nrt( const int x ) { return ! isRt( x ); }
void reverse( const int x ) { if( x ) swapp( ch[x][0], ch[x][1] ), rev[x] ^= 1; }
void normalize( const int x ) { if( x && rev[x] ) reverse( ch[x][0] ), reverse( ch[x][1] ), rev[x] = false; }
void tagClean( const int x ) { if( nrt( x ) ) tagClean( fa[x] ); normalize( x ); } void rotate( const int x )
{
if( ! x || isRt( x ) ) return ;
int y = fa[x], z = fa[y], side = chk( x ), son = ch[x][! side];
if( z && nrt( y ) ) ch[z][chk( y )] = x; ch[x][! side] = y, ch[y][side] = son;
if( son ) fa[son] = y; fa[y] = x, fa[x] = z;
upt( y ), upt( x ), upt( z );
} void splay( const int x )
{
tagClean( x );
for( int y ; nrt( x ) ; rotate( x ) )
if( nrt( y = fa[x] ) )
rotate( chk( y ) == chk( x ) ? y : x );
} void access( int x ) { for( int y = 0 ; x ; x = fa[y = x] ) splay( x ), ch[x][1] = y, upt( x ); }
void makeRt( const int x ) { access( x ), splay( x ), reverse( x ); }
int findRt( int x ) { access( x ), splay( x ); while( ch[x][0] ) normalize( x ), x = ch[x][0]; splay( x ); return x; }
void link( const int x, const int y ) { makeRt( x ); if( findRt( y ) == x ) return; fa[x] = y; }
void cut( const int x, const int y )
{
makeRt( x ), access( y ), splay( x );
fa[y] = ch[x][1] = 0, upt( x );
} int query( const int u, const int r )
{
makeRt( N + 1 ), access( u ), splay( N + 1 );
int cur = N + 1, rp = 0; normalize( cur );
while( ch[cur][cur > r] )
{
if( cur == r ) break;
normalize( cur ), cur = ch[cur][cur > r];
if( cur <= r ) rp = MAX( rp, cur );
}
makeRt( rp );
access( u );
splay( rp );
return su[rp] - val[rp];
} void linkto( const int i, const int v )
{
iter nxa = pos[v].lower_bound( i + 1 ),
nxb = pos[v + 1].lower_bound( i );
nxt[i] = MIN( *nxa, *nxb );
val[i] = nxt[i] == *nxb, upt( i ), link( i, nxt[i] );
} void relink( const int i, const int sid )
{
iter cur = pos[sid].lower_bound( i );
if( cur != pos[sid].begin() )
{
int p = *( -- cur );
cut( p, nxt[p] );
linkto( p, a[p] );
}
} int main()
{
int opt, x, y, z;
read( N ), read( Q );
for( int i = 1 ; i <= N ; i ++ ) read( a[i] ), pos[a[i]].insert( i );
for( int i = 1 ; i <= N + 1 ; i ++ ) pos[i].insert( N + 1 );
for( int i = 1 ; i <= N ; i ++ ) linkto( i, a[i] );
while( Q -- )
{
read( opt ), read( x ), read( y );
if( opt == 1 )
{
int lst = a[x]; a[x] = y;
pos[lst].erase( x ), pos[y].insert( x );
relink( x, lst );
relink( x, lst - 1 );
relink( x, y );
relink( x, y - 1 );
cut( x, nxt[x] );
linkto( x, a[x] );
//link
}
else
{
read( z );
iter p = pos[z].lower_bound( x );
int lmost = *p;
if( lmost > y ) puts( "-1" );
else write( query( lmost, y ) ), putchar( '\n' );
}
}
return 0;
}

T3

  构造题目。

  首先发现题目一定有解。

  我们现在在\(A=[0,a)\)和\(B=[b,b+a)\)里面进行匹配。假设它们都在\([0,2^n)\)中。

  如果\(A\cap B\not=\varnothing\),那么我们就可以进行匹配,直到\(A\cap B=\varnothing\)。

  此时\(A\)一定是在\([0,2^{n-1})\)里面(否则\(B\)就会超过\(2^n\))。

  那么\(A\)就可以直接折到\([0,2^{n-1})\)里面来。考虑\(B\)的分布情况。

  1. \(B\)的元素在二进制\(n-1\)位上相同,那么对它们全部模\(2^{n-1}\)之后,它们就落在了区间\([b',b'+a')\)中,可以继续和\(A\)匹配,规模减半。

  2. \(B\)的元素在二进制\(n-1\)位上不同,那么取模之后一定是\([x,2^{n-1})\cup [0,y)\)的形式。我们可以将\([0,y)\)和\(A\)中的元素匹配。剩下的继续匹配,返回的匹配结果可以映射到当前的结果上来。

  代码:

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std; const int MAXN = 1e6 + 5; int N, M; void match( int n, int m, int addn, int addm, vector<int> &res )
{
if( ! n ) return ;
int k = 1;
while( k < n ) k <<= 1;
m &= k - 1;
if( m < n )
{
for( int i = 0 ; i < n - m ; i ++ )
res[i + addm] = i + m + addn;
match( m, n, addn, addm + n - m, res );
}
else
{
int q = k - m;
for( int i = 0 ; i < n + m - k ; i ++ )
res[addm + q + i] = addn + i;
vector<int> tmp( q );
match( q, k - n, 0, 0, tmp );
for( int i = 0 ; i < q ; i ++ )
res[q - tmp[i] - 1 + addm] = n - 1 - i + addn;
}
} int main()
{
scanf( "%d %d", &N, &M );
vector<int> ans( N );
match( N, M, 0, 0, ans );
for( int i = 0 ; i < N ; i ++ )
printf( "%d %d\n", ans[i], i + M );
return 0;
}

[noi.ac省选模拟赛]第11场题解集合的更多相关文章

  1. [noi.ac省选模拟赛]第12场题解集合

    题目 比赛界面. T1 数据范围明示直接\(O(n^2)\)计算,问题就在如何快速计算. 树上路径统计通常会用到差分方法.这里有两棵树,因此我们可以做"差分套差分",在 A 树上对 ...

  2. [noi.ac省选模拟赛]第10场题解集合

    题目 比赛界面. T1 不难想到,对于一个与\(k\)根棍子连接的轨道,我们可以将它拆分成\(k+1\)个点,表示这条轨道不同的\(k+1\)段. 那么,棍子就成为了点与点之间的边.可以发现,按照棍子 ...

  3. NOI.AC省选模拟赛第一场 T1 (树上高斯消元)

    link 很容易对于每个点列出式子 \(f_{x,y}=(f_{x,y-1}+f_{x,y}+f_{x,y+1}+f_{x+1,y})/4\)(边角转移类似,略) 这个转移是相互依赖的就gg了 不过你 ...

  4. [NOI.AC省选模拟赛3.31] 星辰大海 [半平面交]

    题面 传送门 思路 懒得解释了......也是比较简单的结论 但是自己看到几何就退缩了...... 下周之内写一个计算几何的学习笔记! Code #include<iostream> #i ...

  5. [NOI.AC省选模拟赛3.31] 附耳而至 [平面图+最小割]

    题面 传送门 思路 其实就是很明显的平面图模型. 不咕咕咕的平面图学习笔记 用最左转线求出对偶图的点,以及原图中每个边两侧的点是谁 建立网络流图: 源点连接至每一个对偶图点,权值为这个区域的光明能量 ...

  6. [NOI.AC省选模拟赛3.30] Mas的童年 [二进制乱搞]

    题面 传送门 思路 这题其实蛮好想的......就是我考试的时候zz了,一直没有想到标记过的可以不再标记,总复杂度是$O(n)$ 首先我们求个前缀和,那么$ans_i=max(pre[j]+pre[i ...

  7. [NOI.AC省选模拟赛3.23] 染色 [点分治+BFS序]

    题面 传送门 重要思想 真的是没想到,我很久以来一直以为总会有应用的$BFS$序,最终居然是以这种方式出现在题目中 笔记:$BFS$序可以用来处理限制点对距离的题目(综合点分树使用) 思路 本题中首先 ...

  8. [NOI.AC省选模拟赛3.23] 集合 [数学]

    题面 传送门 一句话题意: 给定$n\leq 1e9,k\leq 1e7,T\leq 1e9$ 设全集$U=\lbrace 1,2,3,...n\rbrace $,求$(min_{x\in S}\lb ...

  9. [noi.ac省选模拟赛20200606]赌怪

    题目   点这里看题目. 分析   先特判掉\(K=2\)的情况.   首先可以考虑到一个简单 DP :   \(f(i)\):前\(i\)张牌的最大贡献.   转移可以\(O(n^2)\)地枚举区间 ...

随机推荐

  1. Spring 基于设值函数(setter方法)的依赖注入

    当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数,基于设值函数的 DI 就完成了. 下述例子显示了一个类 Text ...

  2. js动态添加iframe,自适应页面宽高

    function showIframe(url,w,h){ //添加iframe var if_w = w; var if_h = h; $("<iframe width='" ...

  3. SpringBoot2.x快速入门指南(一)

    SpringBoot2.x快速入门指南(一) 准备工作 IDE: IntelliJ IDEA 2020.3 Java环境 jdk1.8 在官网快速创建SpringBoot项目 下面开始进入正题: 进入 ...

  4. MRCTF 2020 WP

    MRCTF 2020 WP 引言 周末趁上课之余,做了一下北邮的CTF,这里记录一下做出来的几题的WP ez_bypass 知识点:MD5强类型比较,is_numeric()函数绕过 题目源码: I ...

  5. Django ListView DetailView等基于类的视图如何添加装饰器?

    场景: Django开发中,如果我们使用了类视图,如:ListView.DetailView.UpdateView等,这时我们又想要对这个视图添加一个装饰器,来实现某种功能,这时候该怎么处理呢? 环境 ...

  6. 【算法基础】KMP字符串

    给定一个模式串S,以及一个模板串P,所有字符串中只包含大小写英文字母以及阿拉伯数字. 模板串P在模式串S中多次作为子串出现. 求出模板串P在模式串S中所有出现的位置的起始下标. 输入格式 第一行输入整 ...

  7. Take advantage of Checkra1n to Jailbreak iDevice for App analysis

    An unpatchable bootrom exploit called "checkm8" works on all iDevices up until the iPhone ...

  8. CSS3新增伪类有那些?

    p:first-of-type 选择属于其父元素的首个元素 p:last-of-type 选择属于其父元素的最后元素 p:only-of-type 选择属于其父元素唯一的元素 p:only-child ...

  9. JAVA实现对称加密

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.对称加密算法DES 1.概述:采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这 ...

  10. Java实现 蓝桥杯 算法提高 特等奖学金(暴力)

    试题 算法提高 特等奖学金 问题描述 土豪大学每年都会给学生颁发巨额的特等奖学金,当然,获奖的前提是要足够优秀.这所大学有n名学生,他们中的每个人都会在m门必修课的每门课上获得一个百分制的整数成绩(0 ...