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 */