练习赛6 AB暴力 C组合数学 E莫队 G计算几何 I floyd K线段树+二分

26 篇文章 0 订阅
20 篇文章 0 订阅

A
题意:每个人有三个值,找出所有满足自己最大的两个值大于所有人最小的两个值的人。
思路:暴力,存所有人最小的值中最大的,和第二小值最大的,分别和每个人最大次大的比较。
Code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#define LL long long
using namespace std;
const int AX = 2e5+66;
struct Node{
    int x ,y , z;
}a[AX];
int b[AX];
int c[AX];
int main(){
    ios_base::sync_with_stdio(false); cin.tie(0) ; cout.tie(0);
    int n ;
    cin >> n ;
    int x , y , z ;
    int tot = 0 ;
    int totc = 0 ;
    for( int i = 0 ; i < n ; i++ ){
        cin >> x >> y >> z;
        if( x > y ) swap(x,y);
        if( x > z ) swap(x,z);
        if( y > z ) swap(y,z);

        b[tot++] = x;
        c[totc++] = y;
        a[i].y = y;
        a[i].z = z;
    }
    int res = 0;
    sort( b , b + tot );
    sort( c , c + tot );

    for( int i = 0 ; i < n ; i++ ){
        if( a[i].y >= b[tot-1] && a[i].z >= c[totc-1] ){
            res ++;
        }
    }
    cout << res << endl;
    return 0 ;
}

B
题意:
设 f(n) 表示 n 的每一位数字的平方之和,求 [a,b] 中有几个 n 满足 k × f(n)=n
思路:18位的数每一位最大是9,平方和加起来不超过1458,直接枚举答案。
Code:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int AX = 1e4+666;
const int MAXN = 1460;
int bit[AX];

LL check( LL x ){
    int tp;
    LL ans = 0LL;
    while( x ){
        tp = x % 10;
        ans += tp * tp;
        x /= 10;
    }
    return ans ;
}

int main(){
    LL k ,a , b;
    cin >> k >> a >> b ;
    int res = 0;
    for( LL i = 0 ; i < MAXN ; i++ ){  
        LL tmp = (LL)k * i ;
        if( tmp >= a && tmp <= b ){
            if( check(tmp) == i ){
                res ++;
            }
        }
    }    
    cout << res << endl;
    return 0;
}

C
题意:小 Hi 手上有 n 张面值互不相同的钱币,且面值都是 2 的幂次,现在他想知道,他可以组合出多少种小于等于 c 的正整数金额。
思路:组合数学,因为每个数都不同且是2的幂,相当于知道了哪位可以为1,然后对于给出的c,只需要从每次从c当前的最高位枚举,
遇到1的话,可以假设:
当前为0,那么剩下位数中可以为1的可以随便放1或者0,结果都是小于c
当前为1,如果这位可以放1,那么继续枚举,不可以放1的话就结束了。
Code:

#include <bits/stdc++.h>
#define LL unsigned long long
using namespace std;
const int AX = 100;
int num[AX];
int sum[AX];
LL a[AX];
int main(){
    ios_base::sync_with_stdio(false); cin.tie(0) ; cout.tie(0);
    int n ;
    LL c ,x ;
    cin >> n >> c;
    for( int i = 0 ; i < n ; i++ ){
        cin >> x;
        for( int j = 0 ; j <= 64 ; j++){
            if( (1ULL<<j) & x ){
                num[j] ++;
                break;
            }
        }
    }
    sum[0] = num[0];
    for( int i = 1 ; i <= 64 ; i++ ){
        sum[i] = sum[i-1] + num[i];
    }
/*  for( int i = 0 ; i <= 64 ; i++ ){
        cout << sum[i] << endl;
    }*/

    LL tmp = c;
    int bit[AX];
    int tot = 0;
    while( tmp ){
        bit[tot++] = tmp % 2;
        tmp /= 2 ;
    }
    LL res = 0ULL;
    res = (1ULL<<(sum[tot-2])) - 1 ;
    if( num[tot-1] ){
        for( int i = tot - 2 ; i >= 0 ; i -- ){
            if( !bit[i] ) continue;
            res += ( 1ULL<<( (i-1 < 0) ? 0 : sum[i-1]) )  ;
            if( !num[i] ){
                break;
            }
        }       
    }
    cout << res << endl;
    return 0;
}

E
题意:求每个区间每个数字出现次数的平方*这个数字的和
思路:莫队离线查询。
Code:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int AX = 1e6+666;
LL a[AX];
int b[AX];
struct Node{
    int l , r ;
    int id ;
    bool friend operator < ( const Node &x , const Node &y ){
        return b[x.l] == b[y.l] ? x.r < y.r : x.l < y.l ;
    }
}s[AX];
LL res[AX];
LL num[AX];
LL ans ;

void solve( int x , int add ){
    ans -= x * (LL) num[x] * num[x];
    num[x] += add;
    ans += x * (LL) num[x] * num[x];
}

int main(){
    //ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n , t ;
    scanf("%d%d",&n,&t);
    for( int i = 1 ; i <= n ; i++ ){
        scanf("%I64d",&a[i]);
    }
    int len = sqrt(n*1.0) ;
    for( int i = 1 ; i <= n ; i++ ){
        b[i] = ( i - 1 ) / len + 1 ;
    }
    for( int i = 1 ; i <= t ; i++ ){
        scanf("%d%d",&s[i].l,&s[i].r);
        s[i].id = i ;
    }
    sort( s + 1 , s + t + 1 );
    ans = 0LL;
    int l = 0 , r = 0 ;
    for( int i = 1 ; i <= t ; i++ ){
        while( l < s[i].l ) solve( a[l++] , -1 ) ;
        while( l > s[i].l ) solve( a[--l] , 1  ) ;
        while( r < s[i].r ) solve( a[++r] , 1  ) ;
        while( r > s[i].r ) solve( a[r--] , -1 ) ;

        res[s[i].id] = ans ;
    }
    for( int i = 1 ; i <= t ; i++ ){
        printf("%I64d\n",res[i]);
    }
    return 0 ; 
}

G
题意:求表面积。
思路:我是求出总共的面数减去遮盖的。分别从6个面考虑遮盖。
Code:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int AX = 1e2+66;
int a[AX][AX];
char s[AX][AX];
int n , m;
LL cal( int x , int y ){
    LL ans = 0 ;
    if( a[x][y] > 1 ){
        ans += ( ( a[x][y] - 1 ) * 2 );
    }
    int x1 = x - 1 ;
    int y1 = y - 1 ;
    int x2 = x + 1 ;
    int y2 = y + 1 ;
    if( x1 >= 0 && a[x1][y] ){
        ans += min( a[x1][y], a[x][y]) ;
    }
    if( x2 < n && a[x2][y] ){
        ans += min( a[x2][y] , a[x][y] );
    }
    if( y1 >= 0 && a[x][y1] ){
        ans += min( a[x][y1], a[x][y] );
    }
    if( y2 < m && a[x][y2] ){
        ans += min( a[x][y2], a[x][y] );
    }
    return ans ;
}

int main(){
    ios_base::sync_with_stdio(false); cin.tie(0) ; cout.tie(0);
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    cin >> n >> m ;
    LL sum = 0LL;
    for( int i = 0; i < n ; i++ ){
        cin >> s[i];
    }
    for( int i = 0 ; i < n ; i++ ){
        for( int j = 0; j < m ; j++ ){
            a[i][j] = s[i][j] - '0';
            sum += a[i][j];
        }
    }
    sum *= 6;
    for( int i = 0 ; i < n ; i++ ){
        for( int j = 0 ; j < m; j++ ){
            if( !a[i][j] ) continue;
            int ans = cal( i , j );
            //cout << ans << endl;
            sum -= ans ;
        }
    }
    cout << sum << endl;
    return 0;
}

I
题意:给出n个货币,m个汇率转换关系,问能不能通过转换货币从而使任意一种钱币升值。
思路:最长路,汇率有小于1,乘法的小于1相当于加法的负数,所以相当于会有负权,用spfa或者floyd求。

注意:数据由货币自己兑换自己的zz情况。我从每个点搜wa到死。。
Code:

#include <bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 100+66;
int a[AX];
map<string,int>mp;
int tot ;
double dis[AX][AX];

void floyd(){
    for( int k = 1; k <= tot ; k++ ){
        for( int i = 1 ; i <= tot ; i++ ){
            for( int j = 1 ; j <= tot ; j++ ){
                if( dis[i][j] < dis[i][k] * dis[k][j] ){
                    if( dis[i][k] != -INF && dis[k][j] != -INF ){
                        dis[i][j] = dis[i][k] * dis[k][j]; 
                    }
                }
            }
        }
    }
}

int main(){

    string s ;
    int Case = 0;
    int n;
    while( scanf("%d",&n) && n ){
        tot = 0 ;
        mp.clear();
        for( int i = 0 ; i < n ; i++ ){
            cin >> s;
            if( !mp[s] ){
                mp[s] = ++tot;
            }
        }
        int m ;
        scanf("%d",&m);
        string s1,s2;
        double d;
        for( int i = 0 ; i < AX ; i++ ){
            for( int j  = 0 ; j < AX ; j++ ){
                if( i == j ) dis[i][j] = 1.0;
                else dis[i][j] = -INF;
            }
        }
        for( int i = 0 ; i < m; i++ ){
            cin >> s1 >> d >>s2;
            int u = mp[s1];
            int v = mp[s2];
            dis[u][v] = d;
        }
        floyd();
        int f = 0 ;
        for( int i = 1 ; i <= tot ; i++ ){
            if( dis[i][i] > 1.0 ){
                f = 1 ;
                break;
            }
        }
        cout << "Case "<< ++Case <<": ";
        cout << ( f ? "Yes" :"No") <<endl;
    }
    return 0;
}


K
题意:0操作l,r区间降序,1操作l,r区间升序,最后问k位置
思路:二分答案+线段树维护。
原题Hi~ o( ̄▽ ̄)ブ
Code:

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e5+66;
int lazy[AX<<2];
int s[AX<<2];
int a[AX];
int op[AX];
int l[AX];
int r[AX];
int n,k;
int tot;

void pushDown( int rt , int L , int R ){
    if( lazy[rt] != -1 ){
        int mid = ( L + R ) >> 1 ;
        s[rt<<1] = ( mid - L + 1 ) * lazy[rt] ;
        s[rt<<1|1] = ( R - mid ) * lazy[rt];
        lazy[rt<<1] = lazy[rt];
        lazy[rt<<1|1] = lazy[rt];
        lazy[rt] = -1;
    }
    return ;
}


void pushUp( int rt ){
    s[rt] = s[rt<<1] + s[rt<<1|1];
    return ;
}

void build( int l , int r , int rt , int v ){
    lazy[rt] = -1;
    if( l == r ){
        s[rt] =  ( a[l] > v ) ;
        return ; 
    }
    int mid = ( l + r ) >> 1 ;
    build( l , mid , rt << 1 , v );
    build( mid + 1 , r , rt << 1 | 1 , v);
    pushUp(rt);
}

void update( int L , int R , int v , int l , int r , int rt ){
    if( L <= l && R >= r ){
        s[rt] = v * ( r - l + 1 );
        lazy[rt] = v;
        return;
    }
    pushDown(rt,l,r);
    int mid = ( l + r ) >> 1;
    if( L <= mid ) update( L , R , v , l , mid , rt << 1 );
    if( R > mid ) update( L , R , v , mid + 1 , r , rt << 1 | 1);
    pushUp(rt);
}

int query( int L , int R , int l , int r , int rt ){
    if( L <= l && R >= r ){
        return s[rt];
    }
    pushDown(rt,l,r);
    int ans = 0 ;
    int mid = ( l + r ) >> 1;
    if( L <= mid ) ans += query( L , R , l , mid, rt << 1 );
    if( R > mid )  ans += query( L , R , mid + 1, r , rt << 1 | 1 );
    return ans ;
}

int main(){
    int T;
    scanf("%d",&T);
    while( T-- ){
        memset( s , 0 , sizeof(s) );
        int m ;
        scanf("%d%d",&n,&m);
        for( int i = 1 ; i <= n ; i++ ){
            scanf("%d",&a[i]);
        }
        for( int i = 0 ; i < m ; i++ ){
            scanf("%d%d%d",&op[i],&l[i],&r[i]);     
        }
        scanf("%d",&k);
        int li = 1 , ri = n ; 
        while( li < ri ){
            int mid = ( li + ri ) >> 1 ;
            build( 1 , n , 1 , mid );
            for( int i = 0 ; i < m ; i++ ){
                int L = l[i];
                int R = r[i];
                int c = query( L , R , 1 , n , 1 );
                update( L , R , 0 , 1 , n , 1 );
                if( op[i] ){  
                    if( L <= L + c - 1 ){
                        update( L , L + c - 1 , 1 , 1, n , 1 );
                    } 
                }else{
                    if( R - c + 1 <= R ){
                        update( R - c + 1 , R , 1 , 1 , n , 1 );
                    }
                }
            }
            if( query( k , k , 1 , n , 1 ) ){
                li = mid + 1 ;
            }else ri = mid  ;
        }
        printf("%d\n",li);
    }
    return 0 ;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值