2019牛客暑期多校训练营(第七场) ABCDEHJ

目录

 

A.String

B.Irreducible Polynomial

C.Governing sand

D.Number

E.Number

H.Pair

J.A+B problem


A.String

暴力,从最长的开始跑

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

string s;

bool ok(int l,int r){//判断是否是符合要求的串
    if(r==l) return 1;
    string ss="";
    for(int i=l;i<=r;i++) ss+=s[i];
    string str=ss;
    int x=r-l;
    while(x--){
        str+=str[0];
        str.erase(0,1);
        if(str<ss) return 0;
    }
    return 1;
}

int main()
{
    int t;
    cin>>t;
    while(t--){
        cin>>s;
        int lr=s.size();
        int l=0,r=lr-1;
        while(1){
            for(int i=r;i>=l;i--){//从长度最大的开始跑
                if(ok(l,i)){
                    for(int j=l;j<=i;j++) cout<<s[j];
                    cout<<' ';
                    l=i+1;
                    r=lr-1;
                    break;
                }
            }
            if(l>=lr) break;
        }
        cout<<endl;
    }
    return 0;
}

B.Irreducible Polynomial

代数基本定理,实数域内是否可拆分成低次幂的乘积

#include<cstdio>
int main() {
    int n,tp,a[25];
    int cas;
    scanf("%d", &cas);
    while(cas --) {
        scanf("%d", &n);
        for(int i=0; i<=n; scanf("%d",a+i),i++);
        if(n>2||n==2&&a[1]*a[1]>=4*a[0]*a[2])
            printf("No\n");
        else
            printf("Yes\n");
    }
    return 0;
}

C.Governing sand

权值线段树+离散化+前缀和,按价值建树,离散化高度,按高度从低到高把每个高度插进去,枚举以每一个高度为最高的树需要的花费(比这个高度高的所有树的花费,(利用前缀和计算)+高度比这个高度低的所有树里,每次贪心选取最小的花费砍树,使得这些低的树的个数总和小于这个高度的树)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int n;
const int maxn = 1e5+10;
ll sum[maxn], b[maxn];
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll ans;
#define rl (rt<<1)
#define rr (rt<<1|1)
#define imid ll mid = (l+r)>>1;
 
struct trees{
    ll p, sum;//p是个数,sum是价值总和
}tree[210<<2];
 
struct node{
    ll h, c, p;//h高度,c价值,p个数
    bool operator < (const node &a){//按高度排序
        return h < a.h;
    }
}nd[maxn];
 
ll query(ll x, ll l, ll r, ll rt){//查询砍掉前x棵树需要的最小代价,先找左子树再找右子树
    if(tree[rt].p == x) return tree[rt].sum;
    if(l == r) return x * l;
    imid
    if(tree[rl].p >= x) return query(x, l, mid, rl);
    return  tree[rl].sum + query(x-tree[rl].p, mid+1, r, rr);
}
 
void update(ll x, ll y, ll l, ll r, ll rt){//单点更新,价值为x的点插入y棵树
    if(l == r && l == x){
        tree[rt].p += y;
        tree[rt].sum += y*x;
        return;
    }
    imid
    if(x <= mid) update(x, y, l, mid, rl);
    else update(x, y, mid+1, r, rr);
    tree[rt].p = tree[rl].p + tree[rr].p;
    tree[rt].sum = tree[rl].sum + tree[rr].sum;
}
 
int main()
{
    while(~scanf("%d", &n)){
 
        for(int i = 0; i < n; i++){
            cin >> nd[i].h >> nd[i].c >> nd[i].p;
            b[i] = nd[i].h;
        }
 
        //离散化高度
        sort(b, b+n);
        int k = unique(b, b+n) - b;
        for(int i = 0; i < n; i++) nd[i].h = lower_bound(b, b+k, nd[i].h) - b + 1;
 
        //求高度的前缀和
        sort(nd, nd+n);
        memset(sum, 0, sizeof sum);
        for(int i = 0; i < n; i++) sum[nd[i].h] += nd[i].c * nd[i].p;
        for(int i = 2; i <= k; i++) sum[i] += sum[i-1];
 
        nd[n].h = k+10;//最大的高度也需要判断一下
        memset(tree, 0, sizeof tree);
        ans = inf;
        ll t = 1, y = 0, cnt = 0;
        for(int i = 0; i <= n; i++){
            if(nd[i].h > t){
                cnt = sum[k] - sum[t];//所有大于此高度的树都被砍掉
                if(tree[1].p >= y) cnt += query(tree[1].p - y + 1, 1, 200, 1);
                //砍去低的树使得这个高度占一般以上
                ans = min(ans, cnt);
                y = 0;
                for(int j = i-1; nd[j].h == t; j--) update(nd[j].c, nd[j].p, 1, 200, 1);
                t++;
            }
            if(nd[i].h == t) y+=nd[i].p;
        }
        printf("%lld\n", ans);
    }
 
    return 0;
}

D.Number

简单思维题

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n;
    string m;
    cin >> n >> m;
    int x = m.size();
    if(x > n) cout << "T_T" << endl;
    else{
        cout << m;
        n -= x;
        while(n--) cout << 0;
        cout << endl;
    }
    return 0;
}

E.Number

线段树+离散化,把区间离散化,把[l, r] 离散化成 [l, r+1),,避免合并区间的时候出错,跟平常的线段树还是有不一样的吧,因为平时叶节点存的是单点,这个线段树存的是小区间

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

#define rl (rt<<1)
#define rr (rt<<1|1)
#define imid int mid = (l+r)>>1;

int n;
const int maxn = 4e5+10;
int x[maxn], y[maxn], l[maxn], r[maxn], s[maxn<<1];
struct node{
    int l, r, x, num, lz;
//x是这个区间包含了多少个数(r-l),num是区间被添加了多少次,只对叶节点有用,lz是延迟标记
    ll s;//s记录区间里加了多少个数
}tree[maxn<<3];

void build(int l, int r, int rt){//初始化
    if(l == r){
        tree[rt].l = s[l], tree[rt].r = s[l+1];
        tree[rt].x = s[l+1] - s[l];
        return;
    }

    imid
    build(l, mid, rl);
    build(mid+1, r, rr);
    tree[rt].l = tree[rl].l, tree[rt].r = tree[rr].r;
    tree[rt].x = tree[rt].r - tree[rt].l;
}

void down(int rt){//标记下推
    if(tree[rt].lz){
        tree[rl].lz += tree[rt].lz;
        tree[rr].lz += tree[rt].lz;
        tree[rl].s += 1LL*tree[rt].lz * tree[rl].x;
        tree[rr].s += 1LL*tree[rt].lz * tree[rr].x;
        tree[rl].num += tree[rt].lz;
        tree[rr].num += tree[rt].lz;
        tree[rt].lz = 0;
    }
}

void update(int x, int y, int rt){//更新
    if(x <= tree[rt].l && tree[rt].r <= y){
        tree[rt].lz++;
        tree[rt].num++;
        tree[rt].s += 1LL*tree[rt].x;
        return;
    }

    down(rt);
    if(x < tree[rl].r) update(x, y, rl);//不能加等号,因为tree[rl].r是开的,不属于这个区间
    if(y > tree[rr].l) update(x, y, rr);
    tree[rt].s = tree[rl].s + tree[rr].s;
    tree[rt].num = tree[rl].num + tree[rr].num;
}

ll query(ll x, int l, int r, int rt){//查询
    if(l == r){
        ll t = x%tree[rt].num? x/tree[rt].num+1: x/tree[rt].num;
        return t + tree[rt].l - 1;
    }

    down(rt);
    imid
    if(tree[rl].s >= x) return query(x, l, mid, rl);
    else return query(x-tree[rl].s, mid+1, r, rr);
}

int main()
{
    memset(tree, 0, sizeof(tree));
    scanf("%d", &n);
    int a1, b1, c1, m1, a2, b2, c2, m2;
    scanf("%d %d %d %d %d %d %d %d %d %d %d %d", &x[1], &x[2], &a1, &b1, &c1, &m1, &y[1], &y[2], &a2, &b2, &c2, &m2);

    int cnt = 0;
    for(int i = 1; i <= n; i++){
        if(i > 2){
            x[i] = (1LL*a1 * x[i-1] + 1LL*b1 * x[i-2] + c1) % m1;
            y[i] = (1LL*a2 * y[i-1] + 1LL*b2 * y[i-2] + c2) % m2;
        }
        l[i] = min(x[i], y[i]) + 1, r[i] = max(x[i], y[i]) + 2;//右区间开放
        s[++cnt] = l[i], s[++cnt] = r[i];
    }
    
    sort(s+1, s+cnt+1);//离散化
    cnt = unique(s+1, s+cnt+1) - (s+1);
    build(1, cnt-1, 1);

    ll ans = 0;
    for(int i = 1; i <= n; i++){
        update(l[i], r[i], 1);
        ans += r[i] - l[i];
        printf("%lld\n", query((ans + 1)>>1, 1, cnt-1, 1));
    }
	return 0;
}

H.Pair

数位dp,就把十进制改成二进制,从高到低判断两个数的大小

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll dp[33][4][4][3][3];
ll n1[33], n2[33], n3[33];

//pos是位数,x1和x2表示状态,1可以,2不行,0不确定,l1和l2表示当前位数的数字有没有限制
ll dfs(int pos, int x1, int x2, bool l1, bool l2){
    if(pos == -1) return x1 == 1 || x2 == 1;
    if(x1 == 2 && x2 == 2) return 0;//都不行直接退出
    if(dp[pos][x1][x2][l1][l2] != -1) return dp[pos][x1][x2][l1][l2];
    int up1 = l1? n1[pos]: 1, up2 = l2? n2[pos]: 1;
    ll ans = 0;
    for(int i = 0; i <= up1; i++){
        for(int j = 0; j <= up2; j++){
            int p = x1, q = x2;
            if((i & j) > n3[pos] && x1 != 2) p = 1;
            if((i & j) < n3[pos] && x1 != 1) p = 2;
            if((i ^ j) < n3[pos] && x2 != 2) q = 1;
            if((i ^ j) > n3[pos] && x2 != 1) q = 2;
            ans += dfs(pos-1, p, q, l1&&i==up1, l2&&j==up2);
        }
    }
    dp[pos][x1][x2][l1][l2] = ans;
//函数里没有区分有没有限制,因为每次的dp值都不一样,同样的位数不可能重复,
//之前的数为dp的结果都是一样的,只是查询的区间不一样,所以会分有没有限制
    return ans;
}

ll solve(ll a, ll b, ll c){
    int p1 = 0, p2 = 0, p3 = 0;
    memset(n1, 0, sizeof n1);
    memset(n2, 0, sizeof n2);
    memset(n3, 0, sizeof n3);
    while(a){
        n1[p1++] = a % 2;
        a>>=1;
    }
    while(b){
        n2[p2++] = b % 2;
        b>>=1;
    }
    while(c){
        n3[p3++] = c % 2;
        c>>=1;
    }
    return dfs(max(p1, max(p2, p3))-1, 0, 0, 1, 1);
}

int main()
{
    int t;
    cin >> t;
    while(t--){
        ll a, b, c;
        memset(dp, -1, sizeof dp);
        cin >> a >> b >> c;
        cout << solve(a, b, c) - min(a, c-1) - min(b, c-1) -1 << endl;
    }
	return 0;
}

J.A+B problem

把输入的数倒过来求和,再把和倒过来输出

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    int t;
    cin>>t;
    while(t--){
        string a,b;
        cin>>a>>b;
        ll x=0,y=0;
        reverse(a.begin(),a.end());
        reverse(b.begin(),b.end());
        ll z=0;
        for(int i=0;i<a.size();i++){
            x=x*10+a[i]-'0';
        }
        for(int i=0;i<b.size();i++){
            y=y*10+b[i]-'0';
        }
        z=x+y;
        if(!z) cout<<z<<endl;
        else{
            while(z%10==0) z/=10;
            while(z){
                cout<<z%10;
                z/=10;
            }
            cout<<endl;
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值