牛客练习赛14

https://www.nowcoder.com/acm/contest/82#question

A. 爆搜

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll pri[20] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};//16ge
ll dfs(ll now,ll res,ll deep,ll maxx){
    if(res<pri[deep]) return now;
    ll k = 1;
    res/=pri[deep];
    ll anss = now*(k+1);
    while(res>0  && k<=maxx){
        anss = max(anss,dfs(now*(k+1),res,deep+1,k));
        res/=pri[deep];
        k++;
    }
    return anss;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        ll n;
        scanf("%lld",&n);
        ll ans = dfs(1,n,0,40);
        printf("%lld\n",ans);
    }
    return 0;
}

  B预处理以i为左端点,然后跳k次(k=2^0,1,2,3,4,5,6.....)到的下一个点,对于询问l,r就从l开始跳,二进制分解应该跳的次数。复杂度nlogn

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int maxn = 1e6+9;
ll a[maxn];
int rightmost[20][maxn];
int main()
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    ll sum = 0;
    int r = 0;
    for(int i=1;i<=n;i++){
        while(sum+a[r+1]<=k && r+1<=n){
            sum+=a[++r];
 
        }
        rightmost[0][i] = r+1;
        sum-=a[i];
    }
    rightmost[0][n+1] = n+1;
    for(int i=1;i<20;i++){
        for(int j=1;j<=n+1;j++){
            rightmost[i][j] = rightmost[i-1][rightmost[i-1][j]];
        }
    }
    while(m--){
        int ql,qr;
        scanf("%d%d",&ql,&qr);
 
        int ans = 0;
        int fl = 0;
        for(int i=19;i>=0;i--){
           if(rightmost[i][ql]<=qr){
                if(ql==rightmost[i][ql]) fl =1;
                ans+=(1<<i);
                ql = rightmost[i][ql];
           }
        }
        if(fl==1) puts("Chtholly");
        else printf("%d\n",ans+1);
    }
    return 0;
}

  C题

求区间内极长连续段的长度是1-10的个数,首先考虑离线做法,把询问按右端点排序,如果我们能在log的时间内处理在i处新加的值x对左端点在前一个x的位置和i之间的右端点处理就行了,首先考虑用前缀和维护区间加法,对[l,r]区间加一等价于l处+1 r+1处-1询问时求前缀和就醒了。

然后是记x之前出现的位置是last 那么我们的任务就是维护右端点在i处 左端点在[last+1,i]的正确值,本来是想k^2维护的,因为可以是左边1个右边1到k个,然后发现这个性质具有单调性,就是如果234不行那么1234肯定不行,所以可以在2k次就把[last+1,i]区间分割,区间分割的依据就是要求这区间恰好是这个极长连续断最大的区间,比如如果当前加入10这个值,那么肯定的我要找到cur=max(last,max(pre[9],pre[11]))的位置,这样,当我们修改[cur+1,i]就是极长连续段只有10所影响的左端点区间,然后我们下一次要分割的区间就是[last+1,cur] 那么cur这个点一定是会包含的,所以我们要更新左极远和右极远的大小,比如如果9在[cur,i]之间的话那么ll++,假设这样结束了那么下一次维护的区间就是cur = max(last,max(pre[8],pre[11]))[cur,上一次的cur] 不断的分割[last+1,cur]直到区间大小为0结束,或者是ll于rr都大于10就没必要维护了

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int maxn = 1e6+9;
int a[maxn];
int pre[maxn];
int bitt[11][maxn];

struct node {
    int l,r,id;
     bool operator <(node const &a) const {
        return r<a.r;
    }
}qq[maxn];
char ans[maxn][15];
int lb(int x){return x&(-x);}
void add(int *ppp,int pos,int val){
    while (pos<maxn) {
        ppp[pos]+=val;
        pos+=lb(pos);
    }
}
int Q(int *ppp,int pos){
    int ret = 0;
    while (pos) {
        ret+=ppp[pos];
        pos-=lb(pos);
    }
    return ret;
}
void modify(int k,int l,int r,int val){
    add(bitt[k], l, val);
    add(bitt[k], r+1, -val);
}
int main(int argc, const char * argv[]) {
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&qq[i].l,&qq[i].r);
        qq[i].id = i;
    }
    sort(qq+1,qq+1+m);
    for(int i=1,j=1;i<=n;i++){
        int last = pre[a[i]];
        int ll = 0,rr = 0,cur=i;
        while (last<cur) {
            int pp = last;
            if( ll<=10 && a[i]-ll-1>=1) pp = max(pp,pre[a[i]-ll-1]);
            if( rr<=10 && a[i]+rr+1<=1000000) pp = max(pp,pre[a[i]+rr+1]);
            if(ll && ll<=10) modify(ll, pp+1, cur, -1);
            if(rr && rr<=10) modify(rr, pp+1, cur, -1);
            if(ll+rr+1<=10) {
                modify(ll+rr+1, pp+1, cur, 1);
            }
            
            while (ll<=10 && a[i]-ll-1>=1 && pre[a[i]-ll-1]>=pp) {
                ll++;
            }
            while (rr<=10 && a[i]+rr+1<=1000000 && pre[a[i]+rr+1]>=pp) {
                rr++;
            }
            if(ll>10 && rr>10) break;
            cur = pp;
        }
        
        while (j<=m && qq[j].r==i) {
            for(int k=1;k<=10;k++){
                ans[qq[j].id][k] ='0' + Q(bitt[k],qq[j].l)%10;
            }
            j++;
        }
        pre[a[i]] = i;
    }
    for(int i=1;i<=m;i++){
        puts(ans[i]+1);
    }
    return 0;
}

  

  D题水题

  E题压位bfs 预处理出距离点i是0,1,2,3.。。。n的点,复杂度不会算

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+9;
bitset<maxn> bb[maxn][maxn];
bitset<maxn> vis;
int tot =0;
int head[maxn];
struct edge{
    int v,nex;
}e[100000*2];
vector<int> vv[maxn];
void addedge(int u,int v){
    vv[u].push_back(v);
}
struct node{
    int p,deep;
};
void  bfs(int root){
    vis.reset();
    queue<node> q;
    vis.set(root);
    q.push((node){root,0});
    while(!q.empty()){
        node now = q.front();
        q.pop();
        bb[root][now.deep].set(now.p);
        for(int i=0;i< vv[now.p].size() ;i++){
            int v = vv[now.p][i];
            if(!vis.test(v)){
                vis.set(v);
                q.push((node){v,now.deep+1});
            }
        }
    }
}
 
int main()
{
    int n,m,q;
    memset(head,-1,sizeof(head));
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
    }
    for(int i=1;i<=n;i++){
        bfs(i);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<n;j++){
            bb[i][j]  = bb[i][j-1]|bb[i][j];
        }
    }
    while(q--){
        int t;
        scanf("%d",&t);
        bitset<maxn> tmpp;
        while(t--){
            int x,y;
            scanf("%d%d",&x,&y);
            tmpp|=bb[x][min(y,n-1)];
        }
        printf("%d\n",tmpp.count());
    }
    return 0;
}

  F题留坑

 

posted @ 2018-03-31 22:34  tjucxz  阅读(168)  评论(0编辑  收藏  举报