Codeforces Round #737 (Div. 2)

Codeforces Round #737 (Div. 2)

A - Ezzat and Two Subsequences

给出\(n\)个数分成非空的两组,输出两组分别的平均数之和的最大值。

对于\(100\%\)的数据\(1 \leq n\leq 3\times 10^5\)

将最大数单独作为一组,其他数作为另外一组的情况最优。

考虑将另外一组的任一一个数拿到最大数存在的组,那么最大数的组的平均数变小,另外一组平均数变小。

此时,两组平均数之和的值变小,因此将最大数单独作为一组,其他数作为另外一组的情况最优。

时间复杂度\(O(n)\)

# include <bits/stdc++.h>
# define inf 1e9
# define int long long
using namespace std;
const int N=3e5+10;
int a[N],n;
signed main()
{
	int t; scanf("%lld",&t);
	while (t--) {
		scanf("%lld",&n);
		int mx=-inf,sum=0;
		for (int i=1;i<=n;i++) scanf("%lld",&a[i]),mx=max(mx,a[i]),sum+=a[i];
		double ans=mx+(double)(sum-mx)/(double)(n-1);
		printf("%.7lf\n",ans);
	}
 
	return 0;
 } 

B - Moamen and k-subarrays

给出\(n,k\),和\(n\)个数\(a_i\)

询问是否能通过一次依次操作可以使得数组递增有序。

  • 将数组分成恰好\(k\)个连续的块。
  • 任意排列\(k\)个块
  • \(k\)个块依次排列构成新数组。

对于\(100\%\)的数据\(1 \leq k\leq n \leq 10^5, |a_i|\leq 10^9\)\(a_i\)的值互不相同。

\(n\)个数离散化,由于\(a_i\)的互异性,最终有序的数组为\(1-n\)依次排列。

考虑最长的连续的数字作为一个块,最优方案是这些块交换位置。

最优方案时,所需要的块数最少。

当然,使用更多块甚至分成\(n\)块也能完成任务,这是由于一个连续最长块,可以任意分成独立的若干块。

当然,使用更少的块不能完成排序,因为这样做势必造成至少一次在同一块中相邻的两个数不是连续的。显然通过交换操作,不能让这两个数造成分离,不能完成排序。

时间复杂度\(O(n log_2 n)\)

# include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
map<int,int>mp;
int main()
{
	int t; scanf("%d",&t);
	while (t--) {
		int n,k; scanf("%d%d",&n,&k);
		mp.clear();
		for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
		sort(b+1,b+1+n);
		for (int i=1;i<=n;i++) mp[b[i]]=i;
		for (int i=1;i<=n;i++) a[i]=mp[a[i]];		
		int ans=n;
		for (int i=1;i<n;i++) if (a[i+1]==a[i]+1) ans--;
		puts(k>=ans?"Yes":"No"); 
	} 
	return 0;
}

C - Moamen and XOR

\(n\)个数字构成的数组\(a_i\),给出\(k\),满足\(0 \leq a_i < 2^k\).

满足\(a_1 \& ... \& a_n \geq a_1 \oplus ... \oplus a_n\)

求出\(a_i\)数组的个数,对\(10^9+7\)的取模。

对于\(100\%\)的数据\(1 \leq n\leq 2\times 10^5, 0 \leq k\leq 2\times 10^5\)

从高二进制位到低二进制位考虑这\(n\)个数的问题,

满足\(a_1 \& ... \& a_n \geq a_1 \oplus ... \oplus a_n\),就是有这样两种情况:

  • \(a_1 \& ... \& a_n = a_1 \oplus ... \oplus a_n\)
  • \(a_1 \& ... \& a_n > a_1 \oplus ... \oplus a_n\)

对于所有\(n\)个数的第\(i\)个二进制位,若可以确定此时\(a_1 \& ... \& a_n > a_1 \oplus ... \oplus a_n\)就无须进行后面的比较。

这个时候只有这种可能,第\(i\)个二进制位上\(a_1 \& ... \& a_n = 1\)并且$ a_1 \oplus ... \oplus a_n = 0$.

只有\(n\)为偶数时,才可能在某个二进制位上存在\(a_1 \& ... \& a_n > a_1 \oplus ... \oplus a_n\).

因此,只有\(n\)位偶数时,才有可能\(a_1 \& ... \& a_n > a_1 \oplus ... \oplus a_n\).

对于所有\(n\)个数的第\(i\)个二进制位,若可以确定此时\(a_1 \& ... \& a_n = a_1 \oplus ... \oplus a_n\)就进行后面的比较。

这里有两种可能:

  • \(a_1 \& ... \& a_n = a_1 \oplus ... \oplus a_n = 1\)
  • \(a_1 \& ... \& a_n = a_1 \oplus ... \oplus a_n = 0\)

分别对应:

  • \(n\)为奇数,\(a_1 = a_2 = ... = a_n = 1\). (情况数为\(0\)或者\(1\),取决于\(n\)的奇偶性)
  • \(a_1 ... a_n\)至少存在偶数个$a_j = 1 $并且至少存在一个\(0\).
    • \(n\)为奇数时情况数为$C_{n}^{0} + C_{n}^{2} + ... C_{n}^{n-1}= 2^{n-1} $
    • \(n\)为偶数时情况数为$C_{n}^{0} + C_{n}^{2} + ... C_{n}^{n} - C_{n}^{n}= 2^{n-1}-1 $

最后的答案有两种构成方案:

  • \(a_1 \& ... \& a_n = a_1 \oplus ... \oplus a_n\)

对于一个二进制位\(i\),当\(n\)为奇数时,答案为\(2^{n-1} + 1\),当\(n\)为偶数时,答案为\(2^{n-1}-1\)

而一共有\(k\)个二进制位需要安排,则任意安排一种的情况总数为:

\(n\)为奇数时:\((2^{n-1}+1)^k\)\(n\)为偶数时:\((2^{n-1}-1)^k\)

  • \(a_1 \& ... \& a_n > a_1 \oplus ... \oplus a_n\)\(n\)为偶数。

从高位到低位枚举决断出\(a_1 \& ... \& a_n > a_1 \oplus ... \oplus a_n\)的位置\(i\)

考虑到\(i\)之前二进制位\(j\)权值高,需要所有第\(j\)个二进制数\(a_1 \& ... \& a_n = a_1 \oplus ... \oplus a_n\),可以参考上面的解法。

考虑到\(i\)二进制位只有\(1\)种可能,即\(a_1 \& ... \& a_n = 1\)

考虑到\(i\)之后二进制位可以任意选取,因此这些位置可以\(0/1\)乱填。

将上述两种答案构成方案相加,即为全部答案。

用快速幂计算\(2^n\),程序时间复杂度\(O(k log_2 n)\)

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int mo=1e9+7;
int pow(int x,int n){
	int ans=1;
	while (n) {
		if (n&1) ans=ans*x%mo;
		x=x*x%mo;
		n>>=1;
	}
	return ans;
}
signed main() {
	int t; scanf("%lld",&t);
	while (t--) {
		int n,k; scanf("%lld%lld",&n,&k);
		int res=pow(2ll,n-1);
		if (n&1) res=res+1; else res=res-1;
		int ans=pow(res,k);
		if (!(n&1)) {
			for (int i=1;i<=k;i++) {
				ans=(ans+pow(res,i-1)*pow(pow(2ll,n),k-i)%mo)%mo;
			}
		}
		printf("%lld\n",ans);	
	}
	return 0;
 } 

D - Ezzat and Grid

\(n\)\(10^9\)列的\(01\)矩阵,初始全为\(0\),一些行上添加连续的\(1\),要求删除最小的行数,满足:

任意相邻的两行至少存在一列上同时为\(1\)

要求删除最小行数的同时输出方案。

对于\(100\%\)的数据\(1 \leq n \leq 3\times 10^5\)

按照操作的行号排序,对于第\(i\)行,设\(f_i\)\(i\)行选取最多保留的行数。

转移方程:\(f_i= max(f_i , f_j + 1) , 1\leq j <i\),且第\(i\)行和第\(j\)行至少存在一列上同时为\(1\)

考虑\(f_i\)更新后面的\(f_j\)的值,将\(i\)行对应的连续区间用\(f_i\)更新,求出所有更新值得最大可以转移。

这样就需要一个支持值域\([1,10^9]\),动态区间更新,支持区间求最大值和方案的数据结构。

动态开点的线段树可以做到\(O(n log_2 10^9)\)时间空降常数较大,笔者尝试写了一下\(MLE\)

# include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int Lim = 1e9;
struct node{
	int l,r;
};
struct tmp {
	int val,id;
};
vector<node>a[N];
struct rec{
	int l,r,ls,rs,val,id;
	tmp tag;
}tr[N<<2];
int tot,n,m,f[N],g[N];
bool vis[N];
int New(int l,int r) {
	++tot;
	tr[tot].l=l; tr[tot].r=r;
	tr[tot].id=-1;
	tr[tot].tag.id=-1;
	return tot;
}
void down(int x) {
	if (tr[x].tag.val > tr[tr[x].ls].val) tr[tr[x].ls].val = tr[x].tag.val,tr[tr[x].ls].id = tr[x].tag.id;
	if (tr[x].tag.val > tr[tr[x].ls].tag.val) tr[tr[x].ls].tag = tr[x].tag;
	if (tr[x].tag.val > tr[tr[x].rs].val) tr[tr[x].rs].val = tr[x].tag.val,tr[tr[x].rs].id = tr[x].tag.id;
	if (tr[x].tag.val > tr[tr[x].rs].tag.val) tr[tr[x].rs].tag = tr[x].tag;
	tr[x].tag=(tmp){0,0};
}
void update(int x,int opl,int opr,int val,int id) {
	if (opl<=tr[x].l&&tr[x].r<=opr) {
		if (val > tr[x].val) tr[x].val=val,tr[x].id=id;
		if (val > tr[x].tag.val) tr[x].tag=(tmp){val,id};
		return ;
	}
	int mid=(tr[x].l+tr[x].r)/2;
	if (!tr[x].ls) tr[x].ls = New(tr[x].l,mid);
	if (!tr[x].rs) tr[x].rs = New(mid+1,tr[x].r);
	down(x);
	if (opl<=mid) update(tr[x].ls,opl,opr,val,id);
	if (opr>mid) update(tr[x].rs,opl,opr,val,id);
	if (tr[x].val < tr[tr[x].ls].val) tr[x].val = tr[tr[x].ls].val,tr[x].id=tr[tr[x].ls].id;
	if (tr[x].val < tr[tr[x].rs].val) tr[x].val = tr[tr[x].rs].val,tr[x].id=tr[tr[x].rs].id;
}
tmp query(int x,int opl,int opr) {
	if (!x) return (tmp) {0,0};
	if (opl<=tr[x].l&&tr[x].r<=opr) {
		return (tmp) {tr[x].val,tr[x].id};
	}
	int mid=(tr[x].l+tr[x].r)/2;
	if (!tr[x].ls) tr[x].ls = New(tr[x].l,mid);
	if (!tr[x].rs) tr[x].rs = New(mid+1,tr[x].r);
	down(x);
	tmp res = (tmp){0,0};
	if (opl<=mid) {
		tmp t=query(tr[x].ls,opl,opr);
		if (t.val > res.val) res = t;
	}
	if (opr>mid) {
		tmp t=query(tr[x].rs,opl,opr);
		if (t.val > res.val) res=t;
	}
	return res;
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++) {
		int id,l,r; scanf("%d%d%d",&id,&l,&r);
		a[id].push_back((node){l,r}); 
	}
	tot=1;
	tr[1].id=-1; tr[1].val=0;
	tr[1].l=1; tr[1].r=Lim;
	for (int i=1;i<=n;i++) {
		for (int j=0;j<a[i].size();j++) {
			int l=a[i][j].l,r=a[i][j].r;
			tmp w = query(1,l,r);
			if (w.val+1>f[i]) {
				f[i]=w.val+1; g[i]=w.id;
			}
		}
		for (int j=0;j<a[i].size();j++) {
			int l=a[i][j].l,r=a[i][j].r;
			update(1,l,r,f[i],i);
		}
	}
	int ans = 0 , ii = -1;
	for (int i=1;i<=n;i++) if (f[i]>ans) {
		ans=f[i] , ii = i; 
	}
	printf("%d\n",n-ans);
	vis[ii]=1;
	while (true) {
		ii = g[ii];
		if (ii <= 0) break;
		vis[ii]=1;
	}
	for (int i=1;i<=n;i++) if (!vis[i]) printf("%d ",i);
	puts("");
	return 0;
 } 
// Runtime Error on Test 15

一个更聪明的办法是,离散化后直接普通线段树维护,时间空间可以做到\(O(n log_2 n)\)

# include <bits/stdc++.h>
using namespace std;
const int N=3e5+10;
const int Lim = 1e9;
struct node{
	int l,r;
};
struct tmp {
	int val,id;
};
vector<node>a[N];
struct rec{
	int val,id;
	tmp tag;
}tr[N<<3];
int tot,n,m,f[N],g[N];
bool vis[N];
void down(int x) {
	if (tr[x].tag.val > tr[2*x].val) tr[2*x].val = tr[x].tag.val,tr[2*x].id = tr[x].tag.id;
	if (tr[x].tag.val > tr[2*x].tag.val) tr[2*x].tag = tr[x].tag;
	if (tr[x].tag.val > tr[2*x+1].val) tr[2*x+1].val = tr[x].tag.val,tr[2*x+1].id = tr[x].tag.id;
	if (tr[x].tag.val > tr[2*x+1].tag.val) tr[2*x+1].tag = tr[x].tag;
	tr[x].tag=(tmp){0,0};
}
void update(int x,int l,int r,int opl,int opr,int val,int id) {
	if (opl<=l&&r<=opr) {
		if (val > tr[x].val) tr[x].val=val,tr[x].id=id;
		if (val > tr[x].tag.val) tr[x].tag=(tmp){val,id};
		return ;
	}
	int mid=(l+r)/2;
	down(x);
	if (opl<=mid) update(2*x,l,mid,opl,opr,val,id);
	if (opr>mid) update(2*x+1,mid+1,r,opl,opr,val,id);
	if (tr[x].val < tr[2*x].val) tr[x].val = tr[2*x].val,tr[x].id=tr[2*x].id;
	if (tr[x].val < tr[2*x+1].val) tr[x].val = tr[2*x+1].val,tr[x].id=tr[2*x+1].id;
}
tmp query(int x,int l,int r,int opl,int opr) {
	if (!x) return (tmp) {0,0};
	if (opl<=l&&r<=opr) {
		return (tmp) {tr[x].val,tr[x].id};
	}
	int mid=(l+r)/2;
	down(x);
	tmp res = (tmp){0,0};
	if (opl<=mid) {
		tmp t=query(2*x,l,mid,opl,opr);
		if (t.val > res.val) res = t;
	}
	if (opr>mid) {
		tmp t=query(2*x+1,mid+1,r,opl,opr);
		if (t.val > res.val) res=t;
	}
	return res;
}
int tt[2*N],T;
int getli(int x) {
    return lower_bound(tt+1,tt+1+T,x)-tt;
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++) {
		int id,l,r; scanf("%d%d%d",&id,&l,&r);
		tt[++tt[0]]=l; tt[++tt[0]]=r;
		a[id].push_back((node){l,r}); 
	}
	sort(tt+1,tt+1+tt[0]);
	T=unique(tt+1,tt+1+tt[0])-tt-1;
	int mx=0;
	for (int i=1;i<=n;i++)
		for (int j=0;j<a[i].size();j++) {
			a[i][j].l=getli(a[i][j].l);
			a[i][j].r=getli(a[i][j].r);
			mx = max(mx,a[i][j].l);
			mx = max(mx,a[i][j].r);
		}
	tot=1;
	for (int i=1;i<=n;i++) {
		for (int j=0;j<a[i].size();j++) {
			int l=a[i][j].l,r=a[i][j].r;
			tmp w = query(1,1,mx,l,r);
			if (w.val+1>f[i]) {
				f[i]=w.val+1; g[i]=w.id;
			}
		}
		for (int j=0;j<a[i].size();j++) {
			int l=a[i][j].l,r=a[i][j].r;
			update(1,1,mx,l,r,f[i],i);
		}
	}
	int ans = 0 , ii = -1;
	for (int i=1;i<=n;i++) if (f[i]>ans) {
		ans=f[i] , ii = i; 
	}
	printf("%d\n",n-ans);
	vis[ii]=1;
	while (true) {
		ii = g[ii];
		if (ii <= 0) break;
		vis[ii]=1;
	}
	for (int i=1;i<=n;i++) if (!vis[i]) printf("%d ",i);
	puts("");
	return 0;
 } 
posted @ 2021-08-11 00:14  Maystern  阅读(165)  评论(0编辑  收藏  举报