第五届新疆省ACM-ICPC程序设计竞赛
G 最长递增长度
求一下最长上升子序列,这里用了一个典型的\(O(n\log n )\)的做法就是模拟栈,并且不断替换栈,使得栈中的元素在保持长度不变的情况下尽可能的小
#include<bits/stdc++.h>
using namespace std;
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
const int N = 5e4+5;
int a[N] , top;
int32_t main() {
int n = read();
a[0] = INT_MIN , top;
for( int x ; n ; n -- ){
x = read();
if( x > a[top] ) top ++ , a[top] = x;
else{
int t = lower_bound( a + 1 , a + 1 + top , x ) - a;
a[t] = x;
}
}
cout << top << "\n";
return 0;
}
D O(n!)
贪心的判断一下哪一个在前面的优惠更就好
#include<bits/stdc++.h>
using namespace std;
pair<double,double>a[100010];
bool cmp( pair<double,double> a , pair<double,double> b ){
return a.first + b.first * a.second < b.first + a.first * b.second;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].first>>a[i].second;
}
sort(a+1,a+1+n,cmp);
double ans=1;
double sum=0;
for(int i=1;i<=n;i++){
sum+=a[i].first*ans;
ans*=a[i].second;
}
printf("%.6f\n",sum);
return 0;
}
B 狂赌之渊
首先如果有 1 的情况就会优先取 1,直到把 1 取完
然后对于 2 是不会主动取的,因为一取就会给对方制造一个 1,除非只有 2 可以取。
所以当取完 1 后,两人会交替取知道吧所有的数全部取为 2。这是当面对一个全部是 2 的情况下,先手一分都拿不到。所以我们统计有多少个一,并且计算一下取到所有都是 2 的情况下谁是先手
#include<bits/stdc++.h>
using namespace std;
int read() {...}
int32_t main() {
int n = read() , cnt = 0 , sum = 0 , res;
for( int i = 1 , x ; i <= n ; i ++ ){
x = read();
if( x == 1 ) cnt ++;
else sum += x - 2;
}
res = ( cnt >> 1 ) + ( cnt & 1 );
if( ( cnt + sum ) & 1 ) res += n - cnt;
cout << res << "\n";
return 0;
}
F 无交集的圆
这里的圆是没有意义的,不如抽象成[p-r,p+r]
的区间。
因为区间是浮点数,所以先对区间做一个离散化。然后按照区间右端点排序,枚举每一个区间,统计所有右端点小于左端点的区间个数,个数累加就是答案。统计的过程可以用树状数组维护一下。
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) ( x & -x )
const int N = 1e5+5;
int n , res , m;
int bit[N*2];
vector<double> ve;
pair< int , int > a[N];
pair< double , double > b[N];
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
bool cmp( pair<int,int> x , pair<int,int> y ){
if( x.second != y.second ) return x.second < y.second;
return x.first < y.first;
}
void add( int x ){
for( int i = x ; i <= m ; i += lowbit(i) )
bit[i] += 1;
}
int get( int x ){
int sum = 0;
for( int i = x ; i ; i -= lowbit(i) )
sum += bit[i];
return sum;
}
int32_t main() {
scanf( "%d" , &n ) , m = 2 * n;
for( auto [ i , p , r ] = tuple{ 1 , 0.0 , 0.0 } ;i <= n ; i ++ ){
scanf("%lf%lf" , &p , &r );
ve.push_back( p - r ) , ve.push_back( p + r );
b[i] = { p - r , p + r };
}
sort( ve.begin() , ve.end() );
for( int i = 1 ; i <= n ; i ++ )
a[i].first = lower_bound( ve.begin() , ve.end() , b[i].first ) - ve.begin() + 1 ,
a[i].second = lower_bound( ve.begin() , ve.end() , b[i].second ) - ve.begin() + 1;
sort( a+1 , a+1+n , cmp );
for( int i = 1 ; i <= n ; i ++ ){
res += get( a[i].first - 1 );
add( a[i].second );
}
cout << res << "\n";
return 0;
}
I 大吉大利
可以枚举一下 1 用了 x 个金币是其他人减少,然后可以\(O(n)\)的判断一下x 个金币是否可以使得所有人都比他小,然后发现 x 是满足二分性的
#include<bits/stdc++.h>
using namespace std;
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
const int N = 1e5+5;
int n , a[N] , b[N];
bool check( int x , int v ){
for( int i = 2 , tmp ; i <= n ; i ++ ){
if( a[i] < v ) continue;
tmp = a[i] - v + 1;
x -= ( tmp + b[i] - 1 ) / b[i];
if( x < 0 ) return 0;
}
return 1;
}
int32_t main() {
n = read();
for( int i = 1 ; i <= n ; i ++ ) a[i] = read();
for( int i = 2 ; i <= n ; i ++ ) b[i] = read();
int l = 0 , r = a[1] , mid , res = -1;
while( l <= r ){
mid = ( l + r ) >> 1;
if( check( mid , a[1] - mid ) ) res = mid , r = mid - 1;
else l = mid + 1;
}
cout << res << "\n";
return 0;
}
H 虚无的后缀
一个数中有 a 个 2,b 个 5 那么 0 的个数就是min(a,b)
,然后cnt2[i],cnt5[i]
分别表示第i
个数 2 和 5 的个数
然后设计dp的状态dp[i][j][k]
表示前i
个数选择了j
个有k
个5的情况下最多有多少个 2
那么状态转移方程就是dp[i][j][k] = max( dp[i-1][j-1][ k - cnt5[i] ] + cnt2[i])
然后这就是一个 01 背包,可以通过倒序枚举省掉一维空间变成dp[j][k]
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 205;
int n , m , cnt5[N] , cnt2[N] , dp[N][N*36];
bitset<N> vis;
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
int32_t main() {
n = read() , m = read();
for( int i = 1 , x , y ; i <= n ; i ++ ){
x = read();
while( x % 5 == 0 )
x /= 5 , cnt5[i] ++ ;
while( x % 2 == 0 )
x /= 2 , cnt2[i] ++ ;
}
for( int i = 0 ; i <= m ; i ++ )
for( int j = 0 ; j <= n * 36 ; j ++ )
dp[i][j] = INT_MIN;
dp[0][0] = 1;
for( int i = 1 ; i <= n ; i ++ )
for( int j = m ; j >= 1 ; j -- )
for( int k = n*36 ; k >= cnt5[i] ; k -- )
dp[j][k] = max( dp[j][k] , dp[ j - 1 ][ k - cnt5[i] ] + cnt2[i] );
int res = 0;
for( int k = 0 ; k <= n*36 ; k ++ )
res = max( res , min( dp[m][k] , k ) );
cout << res << "\n";
return 0;
}
E 斐波那契串
x看似很大,但实际上因为斐波那契的性质,f[i]
表示斐波那契第i
项的长度,当i
取到六十多的时候f[i]>y
了
所以先预处理出f[i]
数组,对于y
判断是在i-1
中还是i-2
中一直递归直到i=1 or i =2
输出答案就好了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int m ;
string a , b;
vector<int> f;
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
void dfs( int x , int y ){
if( x == 1 ){
cout << a[y-1] << "\n";
return;
}
if( x == 2 ){
cout << b[y-1] << "\n";
return;
}
if( f[x-1] >= y ) dfs( x - 1 , y );
else dfs( x - 2 , y - f[x-1] );
}
int32_t main() {
cin >> a >> b;
f.push_back(0) , f.push_back(a.size()) , f.push_back(b.size()) , m = 2;
while( f[m] + f[m-1] <= 1e18 )
f.push_back( f[m] + f[m-1] ) , m ++ ;
for( int T = read() , x , y ; T ; T -- )
x = read() , y = read() , dfs( min( x , m ) , y );
return 0;
}
J 异或的路径
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5 , mod = 1e9+7;
int n , w[N] , res , cnt[20];
vector< pair<int,int> > e[N];
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
void dfs( int x ){
for( auto [ v , val ] : e[x] )
w[v] = w[x] ^ val , dfs( v );
}
int32_t main() {
n = read();
for( int i = 2 , u , w ; i <= n ; i ++ )
u = read() , w = read() , e[u].push_back( { i , w } );
dfs( 1 );
for( int i = 1 ; i <= n ; i ++ )
for( int j = 0 , t = w[i] ; t ; t >>= 1 , j ++ )
if( t & 1 ) cnt[j] ++;
for( int i = 1 ; i <= n ; i ++)
for( int j = 0 ; j < 20 ; w[i] >>= 1 , j ++ )
if( w[i] & 1 ) res = ( res + ( 1 << j ) * ( n - cnt[j] ) % mod ) % mod;
else res = ( res + ( 1 << j ) * cnt[j] % mod ) % mod;
cout << res << "\n";
return 0;
}
A Good 的集合
根据重心的公式$ (\frac{x_a+x_b+x_c}{3},\frac{y_a+y_b+y_c}{3})$可以知道三个点横纵坐标之和对三取模不能同时为零
根据任意取模原理可以把读入点的横纵坐标模三,这样一共只有(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)
九种点,对于每一种点我们至多只能取两个点,所以最多有 18 个点,然后 dfs 枚举取那些点并\(O(n^3)\)判断合法即可
#include<bits/stdc++.h>
using namespace std;
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
int vis[5][5] , res;
vector< pair<int,int> > g , tmp;
bool check(){
if( tmp.size() < 3 ) return true;
for( int i = 0 ; i < tmp.size() ; i ++ )
for( int j = i + 1 ; j < tmp.size() ; j ++ )
for( int k = j + 1 ; k < tmp.size() ; k ++ ){
int p = tmp[i].first + tmp[j].first + tmp[k].first;
int q = tmp[i].second + tmp[j].second + tmp[k].second;
if( p % 3 == 0 && q % 3 == 0 ) return false;
}
return true;
}
void dfs( int x ){
if( x == g.size() ){
if( check() ) res = max( res , (int)tmp.size() );
return;
}
dfs( x + 1 );
tmp.push_back( g[x] ) , dfs( x + 1 ) , tmp.pop_back();
return;
}
int32_t main() {
for( int n = read() , x , y ; n ; n -- ){
x = read() % 3 , y = read() % 3;
if( vis[x][y] < 2 ) vis[x][y] ++ , g.push_back( { x , y } );
}
dfs( 0 );
cout << res << "\n";
return 0;
}