就先从我本身的负责的题目开始吧(node
\(3\ \sec\ |\ 256\ MegaBytes\)c++
给定 \(n\) 本书,每本书有一个阅读时间,而且可能被甲、乙喜欢或不喜欢。要求选择刚好 \(m\) 本书,使得两人喜欢的书都很多于 \(k\) 本,在知足上述条件的前提下,要求阅读时间最短。若是没有可行方案输出 \(-1\)。数组
\(1\le k\le n\le 2\times 10^5,\ \ 1\le t_i\le 10^4\)。app
这题是 \(hard\ version\),比 \(easy\) 版本多了一个 \(m\) 的限制。考虑一下怎么作是最优的。咱们先把全部的书分红 \(4\) 大类:less
$i.\ $ 甲和乙都喜欢的,此时本书的标记应为 \(11\);spa
$ii.\ $ 只有甲喜欢的,此时本书的标记应为 \(10\);code
$iii.\ $ 只有乙喜欢的,此时本书的标记应为 \(01\);排序
$iiii.\ $ 甲和乙都不喜欢的,此时本书的标记应为 \(00\);队列
在读入的时候能够把这四种书分别放进 \(4\) 个优先队列里边,按照 \(t_i\) 从小到大排序。get
以后咱们先在 \(i.\) 里边和 \(ii.+iii.\) 里边取时间较少的,而后判断一下,若是 \(k\) 还有残余,说明咱们放上最好的 \(m\) 本书依然没法知足条件 \(1\),此时直接输出 \(-1\) 便可。不然,咱们看一下 \(m\) 是否还有富余,若是有的话就在剩下全部书中取最小的,以及计算一下若是把一个已经取了的 \(i.\) 换成 \(ii.+iii.\) 是否合算。
#include<bits/stdc++.h> using namespace std; const int N=2e5+5; int n,m,k; vector<int> t,a,b; vector<pair<int,int> > sol,abqsol; priority_queue<pair<int,int>,vector<pair<int,int> >, greater<pair<int,int> > > abq,aq,bq,nq; int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } int solve() { int res=0; while(m && k && !abq.empty()) { res+=abq.top().first,m--,k--; abqsol.push_back(abq.top()),abq.pop(); } while(m>1 && k && !aq.empty() && !bq.empty()) { res+=aq.top().first+bq.top().first,m--,m--,k--; sol.push_back(aq.top()),sol.push_back(bq.top()),aq.pop(),bq.pop(); } if(k) return res=-1; while(m) { int minn=0x3f3f3f3f; m--; if(!nq.empty()) minn=min(minn,nq.top().first); if(!aq.empty()) minn=min(minn,aq.top().first); if(!bq.empty()) minn=min(minn,bq.top().first); if(!abq.empty()) minn=min(minn,abq.top().first); if(!abqsol.empty() && !aq.empty() && !bq.empty()) { int exchangecost=aq.top().first+bq.top().first-abqsol.back().first; if(exchangecost<=minn) { abq.push(abqsol.back()),abqsol.pop_back(); sol.push_back(aq.top()),sol.push_back(bq.top()); aq.pop(),bq.pop();res+=exchangecost;continue; } } res+=minn; if(!nq.empty() && minn==nq.top().first) sol.push_back(nq.top()),nq.pop(); else if(!aq.empty() && minn==aq.top().first) sol.push_back(aq.top()),aq.pop(); else if(!bq.empty() && minn==bq.top().first) sol.push_back(bq.top()),bq.pop(); else if(!abq.empty() && minn==abq.top().first) sol.push_back(abq.top()),abq.pop(); } return res; } int main() { n=read(),m=read(),k=read(); t.resize(n+1),a.resize(n+1),b.resize(n+1); for(int i=1;i<=n;i++) { t[i]=read(),a[i]=read(),b[i]=read(); if(a[i] && b[i]) abq.push(make_pair(t[i],i)); else if(!a[i] && b[i]) bq.push(make_pair(t[i],i)); else if(a[i] && !b[i]) aq.push(make_pair(t[i],i)); else if(!a[i] && !b[i]) nq.push(make_pair(t[i],i)); } int tottime=solve(); printf("%d\n",tottime); if(tottime==-1) return 0; else { for(int i=0;i<sol.size();i++) cout<<sol[i].second<<" "; for(int i=0;i<abqsol.size();i++) cout<<abqsol[i].second<<" "; } return 0; }
\(2\ \sec\ |\ 256\ MegaBytes\)
给定数组 \(a(n)\)。
对于全部的逆序对 \((a_{l1},a_{r1}),(a_{l2},a_{r2}).....\),找出这些逆序对的一个排列并交换数对,要求操做完以后整个数列单调不降,给出一种可行方案。
简单想一下逆排列,你会发现这道题他很相似于冒泡排序,只不过冒泡排序是相邻两个数交换。
这道题怎么转化为相邻两个数交换呢?逆排列能够作到。
#include <bits/stdc++.h> using namespace std; const int M=1005; int n,m,k,a[M],p[M],x[M*M],y[M*M]; int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } struct node { int a,b; bool operator < (const node &r) const { if(a==r.a) return b<r.b; return a<r.a; } } s[M]; signed main() { n=read(); for(int i=1; i<=n; i++) s[i].a=read(),s[i].b=i; sort(s+1,s+1+n); for(int i=1; i<=n; i++) a[s[i].b]=i,p[i]=s[i].b; for(int i=n; i>=1; i--) { for(int j=a[i]+1; j<=i; j++) x[++k]=p[j],y[k]=i; for(int j=a[i]; j<i; j++) p[j]=p[j+1],a[p[j]]=j; } printf("%d\n",k); for(int i=1; i<=k; i++) printf("%d %d\n",x[i],y[i]); return 0; }
\(3\ \sec\ |\ 512\ MegaBytes\)
给定 \(n\) 对珠子,每对珠子内部已经连好了,如今你再来连 \(n\) 条边,每条边的权值为知足 \(2^k\ |\ u\bigoplus v\),最后全部珠子要造成一个环,问最小的边权最大是多少。
\(1\le n\le 5\times 10^5,\ 0\le k\le 20\)。
首先观察出一个性质:一个数 \(x\) 能被最大的 \(2^k\) 整除的 \(k\) 是多少?是 \(2^{lowbit(x)}\)。
这样一来,咱们就能够枚举一下答案,而后看一眼每两个数异或起来的 \(lowbit\) 是多少。此时时间复杂度是 \(\Theta(20\times n^2)\),过不去。
既然这是一个图论题,咱们考虑连边。若是两个数的 \(lowbit=x\),那么咱们就把这两个数都向 \(2^x-1\) 这个虚拟节点连一下边,最后在整个图上跑一次欧拉回路便可。
#include <bits/stdc++.h> using namespace std; const int N=5e5+5; const int M=1<<20; int n,a[N],b[N],deg[M]; int head[N+M+10],tot,sta[N<<2],top; bool vis[N<<2]; int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } struct EDGE { int nxt,to; } edge[N<<2]; inline void add_edge(int u,int v) { edge[++tot].nxt=head[u]; edge[tot].to=v; head[u]=tot; } void dfs(int u) { for(int& i=head[u]; i; i=edge[i].nxt) if(!vis[i]) {int v=edge[i].to;vis[i]=vis[i^1]=1,dfs(v),sta[++top]=v;} } int main() { n=read(); for(int i=1; i<=n; ++i) a[i]=read(),b[i]=read(); for(int i=20; i>=1; --i) { int S=(1<<i)-1; memset(deg,0,sizeof(deg)); for(int j=1; j<=n; ++j) deg[a[j]&S]++,deg[b[j]&S]++; bool flag=false; for(int j=0; j<=S; ++j) if(deg[j]&1) {flag=true;break;} if(flag) continue; memset(vis,0,sizeof(vis)); memset(head,0,sizeof(head)); tot=1; for(int j=1; j<=n; ++j) add_edge(j,(a[j]&S)+n+1),add_edge((a[j]&S)+n+1,j),add_edge(j,(b[j]&S)+n+1),add_edge((b[j]&S)+n+1,j); top=0,dfs(1); int cnt=0; for(int j=1; j<=top; ++j) cnt+=(sta[j]<=n); if(cnt!=n) continue; cout<<i<<endl; for(int j=1; j<=top; ++j) { if(sta[j]<=n) { if(!(j<top && sta[j+1]>n)) exit(0); int s=sta[j+1]-n-1,id=sta[j]; if(!((a[id]&S)==s || (b[id]&S)==s)) exit(0); if((a[id]&S)==s) cout<<id*2<<" "<<id*2-1<<" "; else cout<<id*2-1<<" "<<id*2<<" "; } } return 0; } cout<<0<<endl;for(int i=1; i<=n*2; ++i) cout<<i<<" "; return 0; }
\(1\ \sec\ |\ 256\ MegaBytes\)
给出一个长度为 \(n\) 的序列 \(A\),请你构造一个一样长度的序列 \(B\),知足:
\(i.\) 刚好有 \(y\) 个数字与 \(A\) 相同。
\(ii.\) 再这 \(y\) 个数字中,刚好有 \(x\) 个与 \(A\) 中的数字相同,位置也相同。
\(1\le n\le 1\times10^5,\ 1\le a_i\le n+1\)
观察到这个 \(n+1\) 很是有趣,说明至少有一个数字 \(A\) 中没有出现过,能够用来填补那 \(n-y\) 个空位。
想一下,有 \(y-x\) 个数字不能与 \(A\) 中位置相同,这说明咱们要贪心地努力下降这 \(y-x\) 个数字的打乱难度,即,让出现次数最多的数字的出现次数尽可能少。因此,对于那 \(x\) 个数,咱们每次选择当前出现次数最多的数放上去。剩下的 \(y-x\) 个,直接找机会放便可。
#include<bits/stdc++.h> using namespace std; const int N=1e5+5; int T,n,x,y; vector<int> apptimes[N]; vector<int> ans; vector<pair<int,int> > res; priority_queue<pair<int,int>,vector<pair<int,int> >,less<pair<int,int> > > q; int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') { x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } int main() { T=read(); while(T--) { res.clear(); while(!q.empty()) q.pop(); n=read(),x=read(),y=read()-x; for(int i=1;i<=n+1;i++) apptimes[i].clear(); ans.resize(n+2); ans.clear(); for(int i=1;i<=n;i++) apptimes[read()].push_back(i); int avail=-1; for(int i=1;i<=n+1;i++) { if(!apptimes[i].size()) avail=i; else q.push(make_pair(apptimes[i].size(),i)); } int tmp=x; while(tmp--) { pair<int,int> p=q.top();q.pop(); ans[apptimes[p.second].back()]=p.second; apptimes[p.second].pop_back(); if(apptimes[p.second].size()) q.push(make_pair(apptimes[p.second].size(),p.second)); } int maxn=0; for(int i=1;i<=n+1;i++) { maxn=max(maxn,(int)apptimes[i].size()); for(int j=0;j<(int)apptimes[i].size();j++) res.push_back(make_pair(apptimes[i][j],i)); } if(n-x-y<(maxn<<1)-n+x) {puts("NO");continue;} puts("YES"); int cnt=0; for(int i=0;i<res.size();i++) { maxn%=res.size();if(cnt==y) break; if(res[i].second!=res[maxn].second) ans[res[i].first]=(res[maxn++].second),cnt++; } for(int i=1;i<=n;i++) cout<< (ans[i]?ans[i]:avail) <<" "; puts(""); } return 0; }