Luogu题目选做(三)
P2249
二分答案
直接套用lower_bound()
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n , m , a[N];
inline int read()
{
register int x = 0;
register char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
int main()
{
n = read() , m = read();
for( register int i = 1 ; i <= n ; i ++ ) a[i] = read();
for( register int i = 1 , x , pos ; i <= m ; i ++ )
{
x = read() , pos = lower_bound( a + 1 , a + 1 + n , x ) - a;
printf( "%d " , pos > n || a[pos] > x ? -1 : pos );
}
return 0;
}
Hash
可以直接用Hash储存每个数第一次出现的的位置
#include<bits/stdc++.h>
using namespace std;
const int Mod = 1e5+7;
int n , m ;
vector< pair<int , int > > hash_map[Mod];
inline int read()
{
register int x = 0;
register char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
inline int check( int x , int key )
{
for( auto it : hash_map[key] ) if( x == it.first ) return it.second;
return -1;
}
int main()
{
n = read() , m = read();
for( register int i = 1 , x , key , f ; i <= n ; i ++ )
{
x = read() , key = x % Mod , f = 0;
if( check( x , key ) == -1 ) hash_map[key].emplace_back( x , i );
}
for( register int i = 1 , x ; i <= m ; i ++ ) x = read() , printf("%d " , check( x , x % Mod ) );
return 0;
}
P2068
树状数组模板题
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit( x ) ( x & -x )
const int N = 1e5 + 5;
int n , m ;
ll bit[N];
inline int read()
{
int x = 0;
char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
inline bool opt()
{
char ch = getchar();
while( ch != 'x' && ch != 'y' ) ch = getchar();
return ( ch == 'x' ? 1 : 0 );
}
inline void update( int x , int y )
{
for( int i = x ; i <= n ; i += lowbit( i ) ) bit[i] += y;
}
inline ll get( int x )
{
ll sum = 0;
for( int i = x ; i ; i -= lowbit( i ) ) sum += bit[i];
return sum;
}
int main()
{
n = read() , m = read();
for( int i = 1 , x , y ; i <= m ; i ++ )
{
if( opt() ) x = read() , y = read() , update( x , y );
else x = read() , y = read() , printf( "%lld\n" , get(y) - get( x - 1 ) );
}
return 0;
}
P1637
跑两边树状数组
先正着跑一边算出lower[i]
,代表1
到i-1
中小于a[i]
的数的个数
再倒着跑一遍算出upper[i]
,代表i+1
到n
中大于a[i]
的数的个数
枚举三元中间的一个,1
到i-1
任何一个小于a[i]
的都可以做第一个元,i+1
到n
中任何一个大于a[i]
的都可以做第三个元
所以答案\(ans = \sum_{i=1}^n lower_i \times upper_i\)
对于a[i]
尽管范围很大但总数很小哈希一下就好
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit( x ) ( x & -x )
#define val first
#define id second
const int N = 3e4 + 5;
int n , m , bit[N] , lower[N] , upper[N];
ll a[N] , b[N] , ans;
inline ll read()
{
ll x = 0;
char ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
inline void update( int x , int y )
{
for( int i = x ; i <= m ; i += lowbit( i ) ) bit[i] += y;
}
inline int get( int x )
{
int sum = 0;
for( int i = x ; i ; i -= lowbit( i ) ) sum += bit[i];
return sum;
}
int main()
{
n = read();
for( int i = 1 ; i <= n ; i ++ ) a[i] = b[i] = read();
sort( b + 1 , b + 1 + n );
m = unique( b + 1 , b + 1 + n ) - b - 1;
for( int i = 1 ; i <= n ; i ++ ) a[i] = lower_bound( b + 1 , b + 1 + m , a[i] ) - b;
for( int i = 1 ; i <= n ; i ++ )
{
lower[i] = get( a[i] - 1 );
update( a[i] , 1 );
}
memset( bit , 0 , sizeof( bit ) );
for( int i = n ; i >= 1 ; i -- )
{
update( a[i] , 1 );
upper[i] = n - i + 1 - get( a[i] );
}
for( int i = 1 ; i <= n ; i ++ ) ans += lower[i] * upper[i];
cout << ans << endl;
}
P1276
线段树
可以用线段树来维护所有状态相同的点来降低复杂度,在修改的操作时顺便将一些状态相同的点给合并。
对于线段树的每个节点有四种状态
1
该区间全部是树木2
该区间全部是树苗0
该区间全部为坑-1
该区间内部点状态不同
对于查询,没有必要用线段树进行维护,我们只需要在修改的时候统计结果即可
在修改时,如果该区间为-1
则递归操作两个子节点,并在操作后考虑吧时候能够合并两个子节点为一个节点
至于从0开始,将所有的点偏移一个单位即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
int n , m , ans1 , ans2 ;// ans1 现有多少树苗 , ans2 有多少树苗被砍掉
struct Node
{
int l , r , value;// 0 没有树 1树木 2 树苗 -1不能当作一个整体
Node * left , * right;
Node( int l , int r , int value , Node * left , Node * right ) : l(l) , r(r) , value(value) , left(left) , right(right){};
} * root;
inline int read()
{
register int x = 0 , f = 1 , ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
inline Node * build( int l , int r )
{
if( l == r ) return new Node( l , r , 1 , 0 , 0 );
register int mid = ( l + r ) >> 1;
Node * left = build( l , mid ) , * right = build( mid + 1 , r );
return new Node( l , r , 1 , left , right );
}
inline void modify_cut( int l , int r , Node * cur )
{
register int mid = ( cur -> l + cur -> r ) >> 1;
if( cur -> value != -1 && cur -> value != 1 && cur -> left ) cur -> left -> value = cur -> value , cur -> right -> value = cur -> value;
if( l <= cur -> l && r >= cur -> r )
{
if( cur -> value == 0 ) return ;
if( cur -> value == 1 ) cur -> value = 0;
if( cur -> value == 2 ) cur -> value = 0 , ans1 -= cur -> r - cur -> l + 1 , ans2 += cur -> r - cur -> l + 1;
if( cur -> value == -1 ) cur -> value = 0 , modify_cut( l , r , cur -> left ) , modify_cut( l , r , cur -> right );
}
else
{
if( l <= mid ) modify_cut( l , r , cur -> left );
if( r > mid ) modify_cut( l , r , cur -> right );
if( cur -> left -> value != cur -> right -> value ) cur -> value = -1;
else cur -> value = cur -> left -> value;
}
}
inline void modify_plant( int l , int r , Node * cur )
{
if( cur -> value != -1 && cur -> value != 1 && cur -> left) cur -> left -> value = cur -> right -> value = cur -> value;
if( l <= cur -> l && r >= cur -> r )
{
if( cur -> value >= 1 ) return ;
if( cur -> value == 0 ) cur -> value = 2 , ans1 += cur -> r - cur -> l + 1;
if( cur -> value == -1 ) modify_plant( l , r , cur -> left ) , modify_plant( l , r , cur -> right );
}
else
{
register int mid = ( cur -> l + cur -> r ) >> 1;
if( l <= mid ) modify_plant( l , r , cur -> left );
if( r > mid ) modify_plant( l , r , cur -> right );
if( cur -> left -> value != cur -> right -> value ) cur -> value = -1;
else cur -> value = cur -> left -> value;
}
}
int main()
{
n = read() + 1 , m = read();
root = build( 1 , n );
for( register int i = 1 , op , l , r ; i <= m ; i ++ )
{
op = read() , l = read() + 1 , r = read() + 1;
if( l > r ) swap( l , r );
if( op == 0 ) modify_cut( l , r , root );
else modify_plant( l , r , root );
}
cout << ans1 << endl << ans2 << endl;
return 0;
}
暴力
写完后计算复杂度发现数据太水,暴力模拟都能过,复杂度应该是\(O(n\times L)\)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
int n , m , ans1 , ans2 , a[N];
inline int read()
{
register int x = 0 , ch = getchar();
while( ch < '0' || ch > '9' ) ch = getchar();
while( ch >= '0' && ch <= '9' ) x = ( x << 3 ) + ( x << 1 ) + ch - '0' , ch = getchar();
return x;
}
int main()
{
n = read() + 1 , m = read();
memset( a , 1 , sizeof(a) );
for( register int i = 1 , op , l , r ; i <= m ; i ++ )
{
op = read() , l = read() + 1 , r = read() + 1;
if( op == 0 )
{
for( register int j = l ; j <= r ; j ++ )
{
if( !a[j] ) continue;
if( a[j] == 2 ) ans1 -- , ans2 ++;
a[j] = 0;
}
}
else
{
for( register int j = l ; j <= r ; j ++ )
{
if( a[j] ) continue;
a[j] = 2 , ans1 ++;
}
}
}
cout << ans1 << endl << ans2 << endl;
return 0;
}