第五届新疆省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;
}
posted @ 2022-08-14 21:06  PHarr  阅读(44)  评论(0编辑  收藏  举报