zoukankan      html  css  js  c++  java
  • 2021年8月11日训练笔记

    就先从我自己的负责的题目开始吧(

    (1.Codeforces CF1374E2)​​ 解题报告

    (3 sec | 256 MegaBytes)

    题面概要

    给定 (n) 本书,每本书有一个阅读时间,并且可能被甲、乙喜欢或不喜欢。要求选择恰好 (m) 本书,使得两人喜欢的书都不少于 (k) 本,在满足上述条件的前提下,要求阅读时间最短。如果没有可行方案输出 (-1)

    (1le kle nle 2 imes 10^5, 1le t_ile 10^4)

    解题过程

    这题是 (hard version),比 (easy) 版本多了一个 (m) 的限制。考虑一下怎么做是最优的。我们先把所有的书分成 (4) 大类:

    $i. $ 甲和乙都喜欢的,此时本书的标记应为 (11)​​;

    $ii. $​​ 只有甲喜欢的,此时本书的标记应为 (10)​​;

    $iii. $​​​ 只有乙喜欢的,此时本书的标记应为 (01)​​​;

    $iiii. $​​​ 甲和乙都不喜欢的,此时本书的标记应为 (00)​​​​;

    在读入的时候可以把这四种书分别放进 (4) 个优先队列里边,按照 (t_i) 从小到大排序。

    之后我们先在 (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
    ",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.Codeforces CF1375E) 解题报告

    (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
    ",k);
    	for(int i=1; i<=k; i++) printf("%d %d
    ",x[i],y[i]);
    	return 0;
    }
    
    
    

    (3.Codeforces CF1361C) 解题报告

    (3 sec | 512 MegaBytes)

    题面概要

    给定 (n) 对珠子,每对珠子内部已经连好了,现在你再来连 (n) 条边,每条边的权值为满足 (2^k | uigoplus v),最后所有珠子要形成一个环,问最小的边权最大是多少。

    (1le nle 5 imes 10^5, 0le kle 20)

    解题过程

    首先观察出一个性质:一个数 (x) 能被最大的 (2^k) 整除的 (k) 是多少?是 (2^{lowbit(x)})​。​

    这样一来,我们就可以枚举一下答案,然后看一眼每两个数异或起来的 (lowbit) 是多少。此时时间复杂度是 (Theta(20 imes 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;
    }
    

    (4. Codeforces CF1381C) 解题报告

    (1 sec | 256 MegaBytes)

    题面概要

    给出一个长度为 (n) 的序列 (A),请你构造一个同样长度的序列 (B),满足:

    (i.) 恰好有 (y) 个数字与 (A) 相同。

    (ii.) 再这 (y) 个数字中,恰好有 (x) 个与 (A) 中的数字相同,位置也相同。

    (1le nle 1 imes10^5, 1le a_ile 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;
    }
    
  • 相关阅读:
    菜鸟浅谈软件开发项目管理
    中国准货币体系的概要简析
    使用dockercompose安装wordpress
    货币乘数
    安全测试的相关内容
    TCP三次握手和四次挥手
    HTTP协议相关
    描述浏览器登录的过程
    AJAX相关知识
    什么是热钱
  • 原文地址:https://www.cnblogs.com/juruo-wsy/p/15131065.html
Copyright © 2011-2022 走看看