zoukankan      html  css  js  c++  java
  • [noi.ac省选模拟赛]第11场题解集合

    题目

      比赛界面

    T1

      比较简单。容易想到是求鱼竿的最大独立集。由于题目的鱼竿可以被分割为二分图,就可以想到最大匹配。
      尝试建边之后会发现边的数量不小,但联系题目性质会发现对于一条鱼竿,它会影响的垂直方向上的鱼竿一定是一个区间,因此再套一发线段树优化即可。
      这里不建议写倍增优化,因为倍增的点是(O(nlog_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( '
    ' );
    	return 0;
    }
    

    T2

      非常比较难受的题目。
      考虑(5sim7)的部分分,可以想到一个做法:设(suf(i))(min{j|jin(i,n],a_j=a_i+1})(不存在则为(n+1))。那么我们求解的过程实际上就是在询问区间的范围内,尝试在(suf)上走最远。
      由于没有修改,因此(suf)也不会变,我们就可以倍增一发,然后询问的复杂度就降下来了。
      时间复杂度(O(nlog_2n)-O(qlog_2n))
      考虑把上面的做法扩展到可修改。不难发现,如果从(i)(suf(i))连边,那么我们可以得到一棵树。于是我们就想到了使用 LCT 。
      不过,还有问题是,如果树变成了菊花图(比如(1,1,1,1,...,2))的情况,那么对根的修改的复杂度将会大得无法接受。
      对于这个问题,我们重新构树——(suf(i))变成(min{j|jin(i,n],a_j=a_i+1lor a_j=a_i})。这样的话,树最多是二叉的,修改的时间就可降下来。我们对于(a_i=a_{suf(i)})的点,将它点权赋 0 ,对于另一类点赋 1 ,这样链上的点权和就可以对应到询问答案。
      用 set 维护每种颜色的点的出现位置。修改的时候,把所有会被影响的点都拉出来改一遍;查询的时候,先找到区间里面第一个(k)的位置(p),再在 LCT 上找出([l,r])区间中最后一个在(p)到根的链上的点,然后求解。
      时间(O(nlog_2n)-O(qlog_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( '
    ' );
    		}
    	}
    	return 0;
    }
    

    T3

      构造题目。
      首先发现题目一定有解。
      我们现在在(A=[0,a))(B=[b,b+a))里面进行匹配。假设它们都在([0,2^n))中。
      如果(Acap B ot=varnothing),那么我们就可以进行匹配,直到(Acap 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
    ", ans[i], i + M );
    	return 0;
    }
    
  • 相关阅读:
    ajax参考增删改查
    linux 断网 扫描基本命令
    linux 基本命令大全
    开发者用的linux系统推荐
    python DRF获取参数介绍
    python DRF操作流程
    python 异常处理函数--raise
    DRF-Rest_Framework 学习文档
    前端框架VUE
    python Django rest-framework 创建序列化工程步骤
  • 原文地址:https://www.cnblogs.com/crashed/p/13096276.html
Copyright © 2011-2022 走看看