2019牛客多校第七场

A String

题意

给定一个01串,求最少的划分次数,使得每部分的01串都是循环字典序最小。

分析

从最长的整个串贪心,暴力判断是否是循环字典序最小,若是,直接输出前面的串,然后后面的串再进行新一轮判断。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=205;
int T;
string s;
vector<int> ans;
bool check(string s){
    int n=s.size();
    for(int i=0;i<n;i++){
        string p=s.substr(i,n-i)+s.substr(0,i);
        if(p<s){
            return false;
        }
    }
    return true;
}
int main(void){
    freopen("in.txt","r",stdin);
    cin >> T;
    while(T--){
        cin >> s;
        int n=s.size();
        while(!s.empty()){
            for(int i=s.size();i>0;i--){
                string t=s.substr(0,i);
                if(check(t)){
                    cout << t << " ";
                    s=s.substr(i,s.size()-i);
                    break;
                }
            }
        }
        cout << "
";
    }
    return 0;
}

B Irreducible Polynomial

题意

给定一个n次多项式,判断能否分解。

分析

n大于2时肯定能分解,n小于2时肯定不能分解,n等于2时韦达定理求根,若有解则说明可分解。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,n;
ll a[25];
int main(void){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=n;i>=0;i--){
            scanf("%lld",&a[i]);
        }
        if(n>2){
            printf("No
");
        }else if(n==2){
            if(a[1]*a[1]>=4ll*a[2]*a[0]){
                printf("No
");
            }else{
                printf("Yes
");
            }
        }else{
            printf("Yes
");
        }
    }
    return 0;
}

C Governing sand

题意

有n种树,每种树有高度,数量,砍一颗的花费三种属性,求最小的砍树花费使得剩下的树中最高的树的数量严格大于总数量的一半。

分析

  • 对树的高度排序,预处理出每种树对应的高度严格大于该高度的树的数量和砍掉这些树的总花费
  • 从小到大枚举每种树作为高度最高的树,严格比它高的树可以直接砍掉,通过前缀和O(1)求出。
  • 严格比它低的树,需要按花费从小到大砍掉(k)棵(k是计算得到的值使得满足数量条件)
  • 使用线段树来维护每个花费((1-200))对应的树的个数和总花费,然后查询前(k)小花费的总和。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls i<<1
#define rs i<<1|1
#define mid (l+r)/2
const int N=1e5+50;
int n;
struct node{
    ll h,p,c;
}a[N];
bool cmp(node a,node b){
    return a.h<b.h;
}
ll hp[N];
ll pp[N];
ll pos[N];
ll siz[N];
ll psz[N];
//线段树维护200个花费对应的数量前缀和,数量乘花费前缀和
ll cnt[200*4],sum[200*4];
void pushup(int i){
    cnt[i]=cnt[ls]+cnt[rs];
    sum[i]=sum[ls]+sum[rs];
}
void build(int i,int l,int r){
    cnt[i]=sum[i]=0;
    if(l==r){
        return;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    pushup(i);
}
//单点更新 价值c多了数量p
void update(int i,int l,int r,int c,int p){
    if(l==r && l==c){
        cnt[i]+=p;
        sum[i]+=1ll*l*p;
        return;
    }
    if(c<=mid){
        update(ls,l,mid,c,p);
    }else{
        update(rs,mid+1,r,c,p);
    }
    pushup(i);
}
//查询花费区间[l,r]前k个对应的花费?
ll query(int i,int l,int r,ll k){
    if(l==r){
        return 1ll*l*k;
    }
    if(cnt[ls]>=k){
        return query(ls,l,mid,k);
    }else{
        return sum[ls]+query(rs,mid+1,r,k-cnt[ls]);
    }
}
int main(void){
//    freopen("in.txt","r",stdin);
    while(~scanf("%d",&n)){
        ll sum=0;
        for(int i=1;i<=n;i++){
            scanf("%lld%lld%lld",&a[i].h,&a[i].c,&a[i].p);
            sum+=a[i].p;
        }
        sort(a+1,a+1+n,cmp);
        hp[n]=0;
        pp[n]=0;
        pos[n]=0;
        psz[n]=0;
        siz[n]=a[n].p;
        for(int i=n-1;i>=1;i--){
            hp[i]=hp[i+1]+a[i+1].p*a[i+1].c;
            pp[i]=pp[i+1]+a[i+1].p;
            pos[i]=hp[i];
            psz[i]=pp[i];
            siz[i]=a[i].p;
            if(a[i].h==a[i+1].h){
                pos[i]=pos[i+1];
                psz[i]=psz[i+1];
                siz[i]+=siz[i+1];
            }
        }
        ll ans=1e18;
        build(1,1,200);
        for(int i=1;i<=n;i++){
            if(a[i].h==a[i-1].h){
                update(1,1,200,a[i].c,a[i].p);
                continue;
            }
            ll tmp=pos[i];
            ll ned=(sum-psz[i]+1)-2ll*siz[i];
             
            if(ned>0){
                tmp+=query(1,1,200,ned);
            }
            ans=min(ans,tmp);
            update(1,1,200,a[i].c,a[i].p);
        }
        printf("%lld
",ans);
    }
    return 0;
}

D Number

题意

给定一个n和p,构造一个能整除p的n位数。

分析

显然当n小于p的位数,无法构造,否则就输出一个p,后面补零直到n位。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll p,n;
int main(void){
    scanf("%lld%lld",&n,&p);
    int pl=0;
    ll t=p;
    while(t){
        pl++;
        t/=10;
    }
    if(n<pl){
        printf("T_T
");
    }else{
        printf("%lld",p);
        for(int i=0;i<n-pl;i++){
            printf("0");
        }
        printf("
");
    }
    return 0;
}

E Find the median

题意

先构造出两个序列(L)(R),初始化一个空序列,然后每次加入(L_i...R_i),并输出中位数。

分析

  • 不考虑值域范围,本质就是区间更新(累加1),求第k个数。
  • 考虑左闭右开区间离散化,建权值线段树,维护线段树节点所对应离散化区间所对应的实际区间的数字个数,以及数字总个数,比如,节点[1,2]对应实际值域区间为[1,4],左闭右开,即[1,5),因此cnt维护数字个数即1 2 3 4共4个数,sum维护总个数,比如这个区间更新了两次,1 1 2 2 3 3 4 4共8个数
  • 维护cnt是为了查询到叶子节点时用来确定是具体哪个数。
  • 维护sum是为了整体查询的时候确定在哪个节点,类似主席树的第k大。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+50;
ll L[N],R[N];
ll x[N],y[N];
ll a[3],b[3],c[3],m[3];
int n;
struct Dcz{
    #define type ll
    vector<type> a;
    void init(){
        a.clear();
    }
    void add(type x){
        a.push_back(x);
    }
    void work(){
        sort(a.begin(),a.end());
        a.erase(unique(a.begin(),a.end()),a.end());
    }
    int pos(type x){
        return lower_bound(a.begin(),a.end(),x)-a.begin()+1;
    }
    type val(int pos){
        return a[pos-1];
    }
    int size(){
        return a.size();
    }
    #undef type
}d;
struct SegTree{
    #define ls i<<1
    #define rs i<<1|1
    #define mid (l+r)/2
    //sum维护左开右闭区间总个数(4,4,5,5,6,6,7,7) l=4 r=9 sum[i]=8
    ll sum[N*4],tag[N*4];
    ll get(int pos){
        return d.val(pos);
    }
    void pushup(int i){
        sum[i]=sum[ls]+sum[rs];
    }
    void build(int i,int l,int r){
        tag[i]=0;
        if(l==r){
            sum[i]=0;
            return;
        }
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(i);
    }
    void pushdonw(int i,int l,int r){
        if(tag[i]){
            tag[ls]+=tag[i];
            tag[rs]+=tag[i];
            //左开右闭
            sum[ls]+=tag[i]*(get(mid+1)-get(l));
            sum[rs]+=tag[i]*(get(r+1)-get(mid+1));
            tag[i]=0;
        }
    }
    void update(int i,int l,int r,int ql,int qr){
        if(ql<=l && qr>=r){
            sum[i]+=get(r+1)-get(l);
            tag[i]++;
            return;
        }
        pushdonw(i,l,r);
        if(ql<=mid){
            update(ls,l,mid,ql,qr);
        }
        if(qr>mid){
            update(rs,mid+1,r,ql,qr);
        }
        pushup(i);
    }
    //离散区间[l,r]实际代表的数sum中第k个
    ll query(int i,int l,int r,ll k){
        if(l==r){
            //比如[4,4,5,5,6,6,7,7]中sum为8 get(..)为4 len为2
            ll len=sum[i]/(get(r+1)-get(l));
            return (k-1)/len+get(l);
        }
        pushdonw(i,l,r);
        if(sum[ls]>=k){
            return query(ls,l,mid,k);
        }else{
            return query(rs,mid+1,r,k-sum[ls]);
        }
    }
}st;
int main(void){
    // freopen("in.txt","r",stdin);
    scanf("%d",&n);
    scanf("%lld%lld%lld%lld%lld%lld",&x[1],&x[2],&a[1],&b[1],&c[1],&m[1]);
    scanf("%lld%lld%lld%lld%lld%lld",&y[1],&y[2],&a[2],&b[2],&c[2],&m[2]);
    d.init();
    //总边界[1,1e9+1)
    d.add(1);
    d.add(1e9+1);
    for(int i=3;i<=N;i++){
        x[i]=(a[1]*x[i-1]%m[1]+b[1]*x[i-2]%m[1]+c[1])%m[1];
        y[i]=(a[2]*y[i-1]%m[2]+b[2]*y[i-2]%m[2]+c[2])%m[2];
    }
    for(int i=1;i<=n;i++){
        L[i]=min(x[i],y[i])+1;
        R[i]=max(x[i],y[i])+1;
        //左开右闭离散化
        d.add(L[i]);
        d.add(L[i]+1);
        //R[i]也需要加入,update时候是使用R[i]的离散化位置,更新sum时候才使用R[i]+1
        d.add(R[i]);
        d.add(R[i]+1);
    }
    d.work();
    int ds=d.size();
    st.build(1,1,ds);
    for(int i=1;i<=n;i++){
        st.update(1,1,ds,d.pos(L[i]),d.pos(R[i]));
        printf("%lld
",st.query(1,1,ds,(st.sum[1]+1)/2));    
    }
    return 0;
}

J A+B problem

题意

给定两个数,反转相加后再反转。

分析

签到题

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b;
int t;
int main(void){
    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld",&a,&b);
        ll aa=0,bb=0;
        while(a){
            aa=aa*10+a%10;
            a/=10;
        }
        while(b){
            bb=bb*10+b%10;
            b/=10;
        }
        ll c=aa+bb;
        ll cc=0;
        while(c){
            cc=cc*10+c%10;
            c/=10;
        }
        printf("%lld
",cc);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zxcoder/p/11326703.html