2024牛客多校第三场
磨合上升期,爽!
B
队友做的
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0;bool f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())f^=(ch=='-'); for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48); return f?x:-x; } const int N=105; int n,d,p,res; int h[N]; int gcd(int x,int y){ if (y==0) return x; return gcd(y,x%y); } signed main() { cin >> n >> d; for (int i=1;i<=n;i++) cin >> h[i]; p=h[n]; for (int i=1;i<n;i++) p=gcd(p,h[i]); res=d%p; if (res>p/2) res=p-res; cout << res << endl; return 0; }
L
#include<bits/stdc++.h> using namespace std; const int N=9; char ch[N+5][N+5]; void Kafka() { for(int i=1;i<=N;++i) scanf("%s",ch[i]+1); bool flag=1; for(int i=1;i<=N;putchar('\n'),++i) for(int j=1;j<=N;++j) { if(i>1&&i<N&&j>1&&j<N&&ch[i][j]=='8'&&flag) putchar('8'),flag=0; else putchar('*'); } } signed main() { Kafka(); return 0; }
J
对于这类 "每个点都是1出度,指向的点可以确定" 的题,经典做法是倍增跳
23年的杭电多校也有一道类似的,问走x步在环上走到哪里,那题想到倍增直接做完
这题比较麻烦的地方在于要先预处理出f[i]表示i到的点才能进行倍增
预处理可以用二分/倍增实现
#include<bits/stdc++.h> using namespace std; const int N=1e5; int n,a,b; char s[N+5]; //PART 1: int sum[N+5]; pair<int,bool> f[N+5]; void Get_Prefix() { sum[1]=s[1]^48; for(int i=2;i<=n;++i) sum[i]=sum[i-1]+(s[i]^48); } inline int QueryA(int L,int R){return L<=R?sum[R]-sum[L-1]:0;} inline int QueryB(int L,int R){return L<=R?R-L+1-QueryA(L,R):0;} int check(int L,int R,int A,int B) { if(A+QueryA(L,R)>=a) return 1; else if(B+QueryB(L,R)>=a) return 0; return -1; } int check(int A,int B) { if(A>=a) return 1; else if(B>=a) return 0; return -1; } int Find_Len(int L,int R,int A,int B) { int l=1,r=R-L+1; for(int mid=l+r>>1;l<r;check(L,L+mid-1,A,B)>-1?r=mid:l=mid+1)mid=l+r>>1; return l; } int Find_Nums(int A,int B) { int l=0,r=(2*a-1)/n+1,tmpA=QueryA(1,n),tmpB=QueryB(1,n);; for(int mid=l+r+1>>1;l<r;check(A+mid*tmpA,B+mid*tmpB)>-1?r=mid-1:l=mid) mid=l+r+1>>1; return l; } void Kafka() { cin>>n>>a>>b; cin>>s+1; Get_Prefix(); // for(int i=1;i<=n;++i) printf("sum[%d]=%d\n",i,sum[i]); for(int i=1;i<=n;++i) { if(~check(i,n,0,0)) { int tmp=Find_Len(i,n,0,0); f[i]=make_pair(i+tmp,check(i,i+tmp-1,0,0)); if(f[i].first>n) f[i].first-=n; } else { int nums=Find_Nums(QueryA(i,n),QueryB(i,n)); int A=QueryA(i,n)+nums*QueryA(1,n),B=QueryB(i,n)+nums*QueryB(1,n); if(A>=a) f[i]=make_pair(1,1); else if(B>=a) f[i]=make_pair(1,0); else { int tmp=Find_Len(1,n,A,B); f[i]=make_pair(1+tmp,check(1,tmp,A,B)); if(f[i].first>n) f[i].first-=n; } } } // for(int i=1;i<=n;++i) printf("f[%d]=%d and %d\n",i,f[i].first,f[i].second); } //PART 2: const int LIMIT=18; pair<int,int> g[LIMIT+5][N+5]; int Gcheck(int A,int B) { if(A>=b) return 1; else if(B>=b) return 0; return -1; } int Ans(int x) { int A=0,B=0; for(int i=LIMIT-1;~i;--i) { int Len=1<<i,tmpA,tmpB; tmpA=g[i][x].second,tmpB=Len-tmpA; if(~Gcheck(A+tmpA,B+tmpB)) continue; x=g[i][x].first,A+=tmpA,B+=tmpB; } A+=g[0][x].second; if(A>=b) return 1; else return 0; } void Himeko() { for(int i=1;i<=n;++i) g[0][i]=f[i]; for(int i=1;i<LIMIT;++i) for(int j=1;j<=n;++j) { g[i][j].first=g[i-1][g[i-1][j].first].first; g[i][j].second=g[i-1][j].second+g[i-1][g[i-1][j].first].second; } // for(int i=0;i<LIMIT;++i) for(int j=1;j<=n;++j) printf("g[%d][%d]:%d and %d\n",i,j,g[i][j].first,g[i][j].second); for(int i=1;i<=n;++i) cout<<(char)(Ans(i)^48); } signed main() { Kafka(); Himeko(); return 0; }
A
思路一直假了,在那边用一种求得出方案的nlog做法写模拟,挂了两发后实在找不到返利,开始贪心
贪心的实质就是算供给,运过去需要的最少次数S为 (n-r)/(r-l) ,能提供的总次数为 Σ min( (h[i]-1)/2, S)
Σ min( (h[i]-1)/2, S) >= S*L 即可
证明还是很妙的,数学归纳法
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int x=0;bool f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())f^=(ch=='-'); for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48); return f?x:-x; } const int N=5e5+505; int n,l,r; int h[N],d[N]; bool sol(){ if (n<l) return false; for (int i=1;i<=n;i++){ int x=(h[i]-1)/2; d[1]++; d[x+1]--; } for (int i=1;i<=n;i++) d[i]+=d[i-1]; int cnt=0,sum=0,k=r-l; for (int i=1;i+r<n+1;i+=k){ cnt++; sum+=d[cnt]; if (sum<cnt*l) return false; } return true; } signed main() { cin >> n >> l >> r; for (int i=1;i<=n;i++) h[i]=read(); if (sol()) cout << "Yes" << endl; else cout << "No" << endl; return 0; }
J
给定n块两端都有数字的骨牌,要求排列它们使得任意相邻的两端都不相等,排列时可以指定某个骨牌左右顺序
本来想到了奇怪的图论优化建图去,在那边网络流流了半天没网出来
首先注意到如果两端都不相等,假设有x个,那么这x个怎么排都可以,如果撞了反一下就好 ........①
如果两端都相等,假设有y个,问题转化为给定长度为y的数组a[i](其中a[i]表示第i个数出现的次数)
每次都可以挑两个出来对消,问要怎么构造使得剩下的最少?
经典结论是:每次挑众数和随便一个数去删,只要众数<=sum/2,偶数能消完,奇数剩一个。否则众数>sum/2,一定会有剩余
实现时我用了小根堆,每次pop两次堆头对消
到这里处理完了两端都相同的,不妨假设剩下的为(1,1)
还没用上的骨牌里如果有(2,1) (3,1)(4,1)这种一端相同的也全部可以接上去
最后还剩两端不一样 && 两端都≠1的,这就和 ① 一样了
#include<bits/stdc++.h> using namespace std; const int N = 2e5+5; int n; inline int read() { int x=0;bool f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())f^=(ch=='-'); for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48); return f?x:-x; } int xid[N<<1],tot[N<<1],x[N],y[N]; int cntx=0,flag=1; struct node{ int c,v; /* bool operator */ bool operator <(const node &b)const{return v==b.v?c<b.c:v>b.v;} bool operator >(const node &b)const{return v==b.v?c>b.c:v<b.v;} }; priority_queue<node,vector<node>,greater<node>>q; void solve(){ n=read(); for(int i=1;i<=n;i++){ x[i]=read(),y[i]=read(); xid[++cntx]=x[i]; xid[++cntx]=y[i]; } sort(xid+1,xid+1+cntx); cntx=unique(xid+1,xid+1+cntx)-(xid+1); int sum=0; vector<pair<int,int>>a; for(int i=1;i<=n;i++){ int idx=lower_bound(xid+1,xid+1+cntx,x[i])-(xid); int idy=lower_bound(xid+1,xid+1+cntx,y[i])-(xid); if(x[i]==y[i]){ tot[idx]++; } else a.push_back({idx,idy}); } int most=0,val=-1; for(int i=1;i<2*N;i++){ if(!tot[i]) continue; q.push((node){i,tot[i]}); sum+=tot[i]; if(tot[i]>most){ most=tot[i],val=i; } } int flag=0; int last=-1; vector<pair<int,int>>out; if(!most){ flag=1; for(auto v:a){ if(v.first==last){ out.push_back({v.second,v.first}); last=v.first; continue; } if(v.second==last){ out.push_back({v.first,v.second}); last=v.second; continue; } out.push_back({v.first,v.second}); last=v.second; } } else { if(most<=sum/2){ // 1.clear int left=0; flag=1; while(!q.empty()){ auto u=q.top(); q.pop(); if(q.empty()==true) { left=u.c; break; } auto v=q.top(); q.pop(); out.push_back({u.c,u.c}); out.push_back({v.c,v.c}); last=v.c; u.v--;v.v--; if(u.v) q.push(u); if(v.v) q.push(v); } if(left) { out.push_back({left,left}); last=left; } for(auto v:a){ if(v.first==last){ out.push_back({v.second,v.first}); last=v.first; continue; } if(v.second==last){ out.push_back({v.first,v.second}); last=v.second; continue; } out.push_back({v.first,v.second}); last=v.second; } } else { node tmp; while(!q.empty()){ auto u=q.top(); q.pop(); if(q.empty()==true) { tmp=u; break; } auto v=q.top(); q.pop(); out.push_back({u.c,u.c}); out.push_back({v.c,v.c}); last=v.c; u.v--;v.v--; if(u.v) q.push(u); if(v.v) q.push(v); } out.push_back({tmp.c,tmp.c}); last=tmp.c; tmp.v--; int cnt=0; for(int i=1;i<=n;i++){ if(x[i]==y[i]) continue; int idx=lower_bound(xid+1,xid+1+cntx,x[i])-(xid); int idy=lower_bound(xid+1,xid+1+cntx,y[i])-(xid); if(idx==tmp.c){ out.push_back({idy,idx}); } else if(idy==tmp.c){ out.push_back({idx,idy}); } else cnt++; } if(cnt>=tmp.v) { flag=1; for(int i=1;i<=n;i++){ if(x[i]==y[i]) continue; int idx=lower_bound(xid+1,xid+1+cntx,x[i])-(xid); int idy=lower_bound(xid+1,xid+1+cntx,y[i])-(xid); if(tmp.v&& idx!=tmp.c&&idy!=tmp.c){ out.push_back({idx,idy}); out.push_back({tmp.c,tmp.c}); last=tmp.c; tmp.v--; } else { if(idx==tmp.c || idy==tmp.c ) continue; if(idx!=last){ out.push_back({idx,idy}); last=idy; } if(idy!=last){ out.push_back({idy,idx}); last=idx; } } } } } } if(!flag){ cout<<"NO"<<"\n"; } else { cout<<"YES"<<"\n"; for(auto v:out){ cout<<xid[v.first]<<" "<<xid[v.second]<<"\n"; } } } signed main() { solve(); return 0; }