2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛

1001 ^&^

题目链接
题意:找到最小的c,满足(a^c)&(b^c)最小,a、b给定
思路:将a、b转化二进制,若a、b的某一位都为1,则c的一位一定为1。此外,c的每一位都是任意的。所以C的最小就是a&b。当a&b==1的时候特判为0。

#include <bits/stdc++.h>

typedef long long ll;
using namespace std;

int t;
ll a,b;
void input(){
    scanf("%lld%lld",&a,&b);
    if((a&b)==0)
        printf("1\n");
    else
        printf("%lld\n",a&b);
}

int main(){
    scanf("%d",&t);
    while(t--){
        input();
//        solve();
    }
}

1002 array

题目链接
题意:给你n个数字,范围在[1,n]之间,且保证数字唯一(即给你n个数字的某种排列)。给你两种操作。1、将下标为pos的数字增加1e7。2、查询[1,r]之间,不与a[i]相等并且大于等于k的数值。
思路:当某一位数值增加了1e7,代表原数值空出来可以作为答案,可放在set中查询。对于查询,建后缀主席树查询包含其中,且大于k的值。答案和set中的最小大于k值取min。

#include <bits/stdc++.h>

#define maxn 100005
typedef long long ll;
using namespace std;

int n,m;
int a[maxn];

struct node{
    int l,r,cnt;
    node(){
        l=r=cnt=0;
    }
}T[maxn*30];
int rt[maxn];

int cnt=0;

void update(int &x,int y,int l,int r,int index){
    T[++cnt]=T[y];
    T[cnt].cnt++;
    x=cnt;

    if(l==r)
        return;

    int mid=(l+r)>>1;
    if(index<=mid)
        update(T[x].l,T[y].l,l,mid,index);
    else
        update(T[x].r,T[y].r,mid+1,r,index);
}

int query(int x,int y,int l,int r,int k){
    if(l==r)
        return l;

    int mid=(l+r)>>1;
    int ans=-1;
    if(k<=mid&&T[T[y].l].cnt) ans=query(T[x].l,T[y].l,l,mid,k);
    if(ans!=-1)    return ans;
    if(T[T[y].r].cnt) ans=query(T[x].r,T[y].r,mid+1,r,k);
    return ans;
}

void input(){
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);

    for(i=1;i<=n;i++)
        update(rt[i],rt[i-1],1,n,a[n-i+1]);
}

void solve(){
    int i;
    int opt;
    int t1,t2;
    int ans=0;
    set <int>st;
    for(i=1;i<=m;i++)
    {
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d",&t1);
            t1=ans^t1;
            st.insert(a[t1]);
        }
        else{
            scanf("%d%d",&t1,&t2);
            t1=t1^ans;
            t2=t2^ans;

            ans=query(rt[1],rt[n-t1],1,n,t2);
            if(ans==-1)
                ans=n+1;

            set<int>::iterator it=st.lower_bound(t2);
            if(it!=st.end())
                ans=min(ans,*it);
            printf("%d\n",ans);
        }
    }
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        cnt=0;
        input();
        solve();
    }
} 

1003 K-th occurrence

题目链接
题意:给定一个字符串,区间[l,r]为一个子字符串,寻找这个字符串出现在这个字符串的第k次下标。
思路:用后缀数组跑字符串,二分字符串上下lcp边界,再在这个区间静态求第k大。

#include <bits/stdc++.h>

#define maxn 100005
using namespace std;

const int maxasc=128;

int n,q;
char s[maxn];

struct SA {
    int t1[maxn], t2[maxn], c[maxn];
    int sa[maxn];
    int Rank[maxn];
    int height[maxn];
    int str[maxn];
    int n, m;
    void init(char *s, int m, int n) {
        this->m = m;
        this->n = n;
        for (int i = 0; i < n; ++i) str[i] = s[i];
        str[n] = 0;
    }
    bool cmp(int *r, int a, int b, int l) {
        return r[a] == r[b] && r[a + l] == r[b + l];
    }
    void work() {
        ++n;
        int i, j, p, *x = t1, *y = t2;
        for (i = 0; i < m; ++i) c[i] = 0;
        for (i = 0; i < n; ++i) c[x[i] = str[i]]++;
        for (i = 1; i < m; ++i) c[i] += c[i - 1];
        for (i = n - 1; i >= 0; --i) sa[--c[x[i]]] = i;
        for (j = 1; j <= n; j <<= 1) {
            p = 0;
            //直接利用sa数组排序第二关键字
            //后面的j个数第二关键字为空的最小
            for (i = n - j; i < n; ++i) {
                y[p++] = i;
            }
            for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
            //这样数组y保存的就是按照第二关键字排序的结果
            //基数排序第一关键字
            for (i = 0; i < m; ++i) c[i] = 0;
            for (i = 0; i < n; ++i) c[x[y[i]]]++;
            for (i = 1; i < m; ++i) c[i] += c[i - 1];
            for (i = n - 1; i >= 0; --i) sa[--c[x[y[i]]]] = y[i];
            //根据sa和x数组计算新的x数组
            swap(x, y);
            p = 1; x[sa[0]] = 0;
            for (i = 1; i < n; ++i) {
                x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
            }
            if (p >= n) break;
            //下次基数排序的最大值
            m = p;
        }
        int k = 0;
        --n;
        for (i = 0; i <= n; ++i) Rank[sa[i]] = i;
        //NOild height
        for (i = 0; i < n; ++i) {
            if (k) --k;
            j = sa[Rank[i] - 1];
            while (str[i + k] == str[j + k]) ++k;
            height[Rank[i]] = k; 
        }
    }
    struct RMQ {
        int Min[maxn][30]; 
        int mm[maxn];
        void init(int n, int *b) {
            mm[0] = -1;
            for (int i = 1; i <= n; ++i) {
                mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
                Min[i][0] = b[i];
            }
            for (int j = 1; j <= mm[n]; ++j) {
                for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
                    Min[i][j] = min(Min[i][j - 1], Min[i + (1 << (j - 1))][j - 1]);
                }
            }
        }
        int queryMin(int x, int y) {
            int k = mm[y - x + 1];
            return min(Min[x][k], Min[y - (1 << k) + 1][k]);  
        }
    }rmq;
    void initrmq() {
        rmq.init(n, height);
    }
    int lcp(int x, int y) {
        if (x == y) return 1e9;
        if (x > y) swap(x, y);
        ++x;
        return rmq.queryMin(x, y);   
    }
}sa;

int len;
int L,R;
bool check(int mid,int x){
    if(sa.lcp(mid,x)>=len)
        return 1;
    else
        return 0;
}

int sorted[maxn];

int num[20][maxn];
int val[20][maxn]; 

void Build(int l,int r,int level){
    if(l==r)return;

    int i;
    int mid=(l+r)>>1;
    int isame=mid-l+1;

    for(i=l;i<=r;i++)
    if(val[level][i]<sorted[mid])
        isame--; 

    int ln=l,rn=mid+1;
    for(i=l;i<=r;i++){
        if(i==l)
            num[level][i]=0;
        else
            num[level][i]=num[level][i-1];

        //判断数放入左结点或者右结点 
        if(val[level][i]<sorted[mid] || val[level][i]==sorted[mid]&&isame>0){
            val[level+1][ln++]=val[level][i];
            num[level][i]++;
            if(val[level][i]==sorted[mid])
                isame--;
        }else{
            val[level+1][rn++]=val[level][i];
        }
    }

    Build(l,mid,level+1);
    Build(mid+1,r,level+1);
}

int check(int level,int L,int R,int l,int r,int k){
    if(l==r)
        return val[level][l];

    int leftNum,rightNum,totalNum;
    if(L==l) leftNum=0;
    else leftNum=num[level][L-1];
    rightNum=num[level][R];
    totalNum=rightNum-leftNum;

    int mid=(l+r)>>1;
    if(totalNum>=k){ 
        return check(level+1,l+leftNum,l+rightNum-1,l,mid,k);
    }else{
        int rightindex=mid+1+(L-l-leftNum);
        return check(level+1,rightindex,rightindex+R-L-totalNum,mid+1,r,k-totalNum);
    }
}

void solve(){
    scanf("%d%d",&n,&q);
    scanf("%s",s);

    sa.init(s,maxasc,n);
    sa.work();
    sa.initrmq();

//    for(int i=1;i<=n;i++)
//        cout<<sa.sa[i]<<endl;
//    cout<<endl;

    int i;
    for(i=1;i<=n;i++){
        val[0][i]=sa.sa[i]; 
        sorted[i]=sa.sa[i];
    }

    sort(sorted+1,sorted+1+n);
    Build(1,n,0);        

    int l,r,k;
    for(i=0;i<q;i++){
        scanf("%d%d%d",&l,&r,&k);
        len=r-l+1;
        L=1;R=sa.Rank[l-1];

        int left,right;
        while(L<=R){
            int mid=(L+R)>>1;
            if(check(mid,sa.Rank[l-1]))
                R=mid-1;
            else
                L=mid+1;
        }
        left=L;

        L=sa.Rank[l-1];R=n;
        while(L<=R){
            int mid=(L+R)>>1;
            if(check(mid,sa.Rank[l-1]))
                L=mid+1;
            else
                R=mid-1;
        }
        right=R;

//        cout<<left<<' '<<right<<endl;
        if(right-left+1<k)
            printf("-1\n");
        else
            printf("%d\n",check(0,left,right,1,n,k)+1);

//        for(int j=left;j<=right;j++)
//            cout<<sa.sa[j]<<endl;
    }
}


int main() {
    int t;
    scanf("%d",&t);
    while(t--){
//        input();
        solve();
    }
}

1004 path

题目链接
题意:给你一张图,求图中的第k小路径
思路:1、记录最大的maxk,然后维护一个大小为maxk的multiset。2、BFS加上优先队列,维护扩展点最小出边和当前点的下一条边。

#include <bits/stdc++.h>
#include <set>
#include <vector>

#define maxn 50005
typedef long long ll;
using namespace std;

int n,m,q;
ll ans[maxn];

struct edge{
    ll to,cost;
};

bool operator <(const edge &a,const edge &b){
    return a.cost<b.cost;
}

vector<edge> G[maxn];
int k[maxn];
int maxK;
multiset <edge>st;

void input(){
    scanf("%d%d%d",&n,&m,&q);
    int i,j;
    ll u,v,w;
    maxK=0;

    for(i=1;i<=n;i++)
        G[i].clear();
    st.clear();

    for(i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld",&u,&v,&w);
        G[u].push_back((edge){v,w});
    }

    for(i=1;i<=q;i++){
        scanf("%d",&k[i]);
        maxK=max(maxK,k[i]); 
    }

    for(i=1;i<=n;i++){
        sort(G[i].begin(),G[i].end());
        for(j=0;j<G[i].size();j++){
            if(st.size()>=maxK)
            {
                auto it=st.end();
                it--;
                if(G[i][j].cost>=(*it).cost)
                    break;
                else
                    st.erase(it);
            }

            st.insert(G[i][j]);
        }    
    }      
}

void solve(){
    int i,j;
    for(i=1;i<=maxK;i++){
        auto it=st.begin();
        ans[i]=(*it).cost;
        if(i==maxK)break;

        ll v=(*it).to;
        ll w=(*it).cost;
          st.erase(st.begin());

        int len=G[v].size();
        for(j=0;j<len;j++){
            edge temp=(edge){G[v][j].to,G[v][j].cost+w};

            if(st.size()>=maxK-i+1)
            {
                auto it=st.end();
                it--;
                if(temp.cost>=(*it).cost)
                    break;
                else
                    st.erase(it);
            }

            st.insert(temp);
        }
    }

    for(i=1;i<=q;i++)
        printf("%lld\n",ans[k[i]]);
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        input();
        solve();
    }
}

1006 Shuffle Card

题目链接
题意:刚开始n张牌,m次操作将值为x的拿出来放在最前头,求最后顺序
思路:先从后往前跑m次查询,再跑n的原序列。标记值出现过的不用再出现。栈思想

#include <bits/stdc++.h>

#define maxn 100005
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;

int n,m;
int a[maxn];
int b[maxn];
int book[maxn];

void input(){
    scanf("%d%d",&n,&m);
    int i;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);

    for(i=1;i<=m;i++){
        scanf("%d",&b[i]);
    }
}

void solve(){
    int i;
    for(i=m;i>=1;i--){
        if(!book[b[i]])
        {
            printf("%d ",b[i]);
            book[b[i]]=1;
        }    
    }

    for(i=1;i<=n;i++){
        if(!book[a[i]])
        {
            printf("%d ",a[i]);
            book[a[i]]=1;
        }
    }
}

int main(){
    input();
    solve();    
}

1007 Windows Of CCPC

题目链接
题意:给出1阶CCPC的图案。之后每一阶图案的左上、右上、右下都是上一阶图案的复制,左下是上一阶图案的取反。求第k阶图案
思路:分形,暴力

#include <bits/stdc++.h>

#define maxn 1500
typedef long long ll;
using namespace std;

int t;
int n;
char g[11][maxn][maxn];
void input(){
    scanf("%d",&n);
}

void solve(){
    g[1][1][1]='C';
    g[1][1][2]='C';
    g[1][2][1]='P';
    g[1][2][2]='C';

    int cnt,i,j;
    for(cnt=2;cnt<=10;cnt++){
        int len=pow(2,cnt-1);
        for(i=1;i<=len;i++){
            for(j=1;j<=len;j++){
                g[cnt][i][j]=g[cnt-1][i][j];
            }
        }

        for(i=1;i<=len;i++){
            for(j=1;j<=len;j++){
                g[cnt][i][j+len]=g[cnt-1][i][j];
            }
        }

        for(i=1;i<=len;i++){
            for(j=1;j<=len;j++){
                if(g[cnt-1][i][j]=='C')
                    g[cnt][i+len][j]='P';
                else
                    g[cnt][i+len][j]='C';
            }
        }

        for(i=1;i<=len;i++){
            for(j=1;j<=len;j++){
                g[cnt][i+len][j+len]=g[cnt-1][i][j];
            }
        }

    }

    int L=pow(2,n);
    for(i=1;i<=L;i++){
        for(j=1;j<=L;j++)
            printf("%c",g[n][i][j]);
        printf("\n");
    }    
}

int main(){
    scanf("%d",&t);
    while(t--){
        input();
        solve();
    }
}

1008 Fishing Master

题目链接
题意:有n条鱼。每条鱼抓需要k的时间,炖需要t[i]的时间,只有抓完鱼才可以炖,求最小时间。
思路:我们需要花费的时间是,抓第一条鱼的时间k+所有鱼炖的时间+前m条鱼抓和炖之间的空隙时间

#include <bits/stdc++.h>

#define maxn 100005
typedef long long ll;
using namespace std;

int t[maxn];
int n,k;

bool cmp(int a,int b){
    return a>b;
}

void input(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&t[i]);
    }
}

void solve(){
    ll ans=k;
    ll tot=0;
    vector <int>v;
    for(int i=1;i<=n;i++){
        ans+=t[i];
        v.push_back(t[i]%k);
        tot+=t[i]/k;
    }

    sort(v.begin(),v.end(),cmp);
    int cnt=0;
    while(tot<n-1){
        ans+=k-v[cnt];
        cnt++;
        tot++;
    }
    printf("%lld\n",ans);
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        input();
        solve();
    }
}

/*
2
3 5
20 4 3
*/