【简要题解】CF 737 Div2 简要题解

【简要题解】CF 737 Div2 简要题解

退役一年之后还能上分?

A - Ezzat and Two Subsequences

首先\(O(n)\)枚举分成的两个序列的大小分别是多少假设分别是\(x\)\(n-x\)

数学直觉告诉我,小的和小的一起,大的和大的一起,所以我们把前\(x\)大放在一起,剩下的放在一起就行

题解给了证明:

The average of a group of numbers always has a value between the minimum and maximum numbers in that group.

这就很显然了

//@winlere
#include<bits/stdc++.h>
using namespace std;
int qr(){
	int ret=0,c=getchar(),f=0;
	while(!isdigit(c)) f=c=='-',c=getchar();
	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
	return f?-ret:ret;
}
const int maxn=1e5+5;
double data[maxn],pre[maxn],suf[maxn];

int main(){
	ios::sync_with_stdio(0);
	int T=qr();
	while(T--){
		int n=qr();
		for(int t=1;t<=n;++t) data[t]=qr();
		sort(data+1,data+n+1);
		for(int t=1;t<=n;++t) pre[t]=pre[t-1]+data[t];
		suf[n+1]=0;
		for(int t=n;t;--t) suf[t]=suf[t+1]+data[t];
		double ans=-2e9;
		for(int t=1;t<n;++t) ans=max(ans,pre[t]/t+suf[t+1]/(n-t));
		cout<<fixed<<setprecision(10)<<ans<<endl;
	}
	return 0;
}


B - Moamen and k-subarrays

离散化后直接看非连续段的个数,很难说比A题难

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int qr(){
	int ret=0,c=getchar(),f=0;
	while(!isdigit(c)) f=c=='-',c=getchar();
	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
	return f?-ret:ret;
}
const int maxn=1e5+5;
int data[maxn],sav[maxn];

int main(){
	int T=qr();
	while(T--){
		int n=qr(),k=qr(),cnt=0;
		for(int t=1;t<=n;++t) sav[t]=data[t]=qr();
		sort(sav+1,sav+n+1);
		for(int t=1;t<=n;++t) data[t]=lower_bound(sav+1,sav+n+1,data[t])-sav;
		data[n+1]=-114514;
		for(int t=1,r=1;t<=n;t=++r){
			while(data[r+1]==data[r]+1) ++r;
			++cnt;
		}
		puts(cnt<=k?"YES":"NO");
	}
	return 0;
}


C - Moamen and XOR

这道题深刻地揭示了退役多年的我已经变得多么菜了

从二进制高位到低位考虑问题的,高位比较出结果低位随便填了。

\(f(n)\)表示有\(n\)\(bit\),这些\(bit\)的异或和\(=\)并起来的和

\(g(n)\)表示有\(n\)\(bit\),这些\(bit\)异或和\(>\)并起来的和

显然:

\[f(n)=\cases {2^{n-1}-1 & 2|n\\2^{n-1}+1 & otherwise } \\ g(n)=\cases {1 &2|n \\ 0 & otherwise} \]

只需要知道\(2^{n-1}\)是怎么来的

\(n\)个二进制位中,选中奇数个使其为一的方案数=选中偶数个使其为一的。这个结论的证明是,可以构造一一对应的关系:将这些二进制位中的第一个给翻转一下得到偶与奇的对应。

其余的\(+1,-1\)是考虑全\(1\)或全\(0\)的情况

然后枚举是再哪一位比出个\(>\)​的结果出来。注意到因为原题是\(\ge\)​,所以还要加一个全部位都相等的情况。

还要注意\(k=0\)

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int qr(){
	int ret=0,c=getchar(),f=0;
	while(!isdigit(c)) f=c=='-',c=getchar();
	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
	return f?-ret:ret;
}
const int mod=1e9+7;
const int maxn=2e5+5;
typedef long long ll;
int f[maxn],g[maxn];
int MOD(const int&a,const int&b){return 1ll*a*b%mod;}
int MOD(const int&a){return a%mod;}
int MOD(vector<int> ve){int ret=1;for(auto t:ve) ret=MOD(ret,t); return ret;}

int ksm(const int&ba,const ll&p){
	int ret=1;
	for(ll t=p,b=ba;t;t>>=1,b=MOD(b,b))
		if(t&1) ret=MOD(ret,b);
	return ret;
}

void pre(int n){
	f[1]=2; 
	for(int t=2;t<=n;++t)
		if(t&1)	f[t]=ksm(2,t-1)+1;
		else f[t]=ksm(2,t-1)-1,g[t]=1;
}

int main(){
	ios::sync_with_stdio(0);
	pre(2e5);
	int T=qr();
	while(T--){
		int n=qr(),k=qr(),ans=0;
		if(k) {
			for(int t=k;t;--t){
				int ret=MOD({ksm(f[n],k-t),g[n],ksm(2,1ll*(t-1)*n)});
				ans=MOD(ans+ret);
			}
			ans=MOD(ans+ksm(f[n],k));
		}else ans=1;
		cout<<ans<<endl;
	}
	return 0;
}


D - Ezzat and Grid

线段树都写错,我不配叫OIer((

问题转化为图论问题,将满足相邻条件的两行由序数小的向序数大的连一条有向边,问题转化为最长路问题

然而这个复杂度是\(O(n^2)\)的,考虑优化,首先一个问题,如果\(1->2\)合理,并且\(2->3\)合理,并且\(1->3\)合理,那么我们不需要连\(1->3\)了。

考虑什么时候\(1->2,2->3,1->3\)都合理,当且仅当他们有某个相同位置都有"1"

离散化所有位置信息,用线段树维护"在这个位置有'1'的编号最大的行的编号",这个用线段树轻松维护。

这个时候就有人问了,你修改的时候可以用lazy tag复杂度是对的,但是你查询的时候复杂度不是\(O(值域)\)了吗

并非如此,每个我们可以记录一个\(dat\in[0,n]\)表示当前区间是全为一个数,若有则这个数是多少。

考虑那些\(dat=0\)​的节点,这是因为某个位置的编号和周围不一样,这个打破统一的情况只会在修改区间的端点附近产生(这个最开始我没考虑到,之前居然分析成\(O(\log^2m)\)我真的太菜了...),可以发现一次修改操作最多产生$ \log m$​个这样的区间

而我们查询的时候,每次遇到\(dat\),我们就递归下去,而且因为修改的区间和查询的区间一样,这个时候\(dat\)更新了,所以相当于查到一个\(dat=0\)就把这个\(dat\)修改成有值了。

复杂度\(O(m\log m)\)

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>

using namespace std;
int qr(){
	int ret=0,c=getchar(),f=0;
	while(!isdigit(c)) f=c=='-',c=getchar();
	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
	return f?-ret:ret;
}
const int maxn=3e5+5;
vector<int> e[maxn];
int n,m,sav[maxn<<1],cnt,dp[maxn],usd[maxn];

void add(int fr,int to){
	if(fr<=0||to<=0||to==fr) return;
	e[fr].push_back(to);
}

struct QWQ{
	int i,l,r;
	bool operator < (const QWQ&a)const{
		return i<a.i;
	}
}data[maxn];

#define mid ((l+r)>>1)
#define lef l,mid,pos<<1
#define rgt mid+1,r,pos<<1|1

struct XDS{
	int tag,dat;//删你麻痹的uni去死吧!
	void upd(int x){tag=dat=x;}
}seg[maxn<<4];

void pd(int pos){
	if(!seg[pos].tag) return;
	seg[pos<<1].upd(seg[pos].tag);
	seg[pos<<1|1].upd(seg[pos].tag);
	seg[pos].tag=0;
}

void pp(int pos){
	if(seg[pos<<1].dat==seg[pos<<1|1].dat&&seg[pos<<1].dat>0)
		seg[pos].dat=seg[pos<<1].dat;
	else seg[pos].dat=0;
}

void que(int to,int L,int R,int l,int r,int pos){
	if(L>r||R<l) return;
	if(seg[pos].dat||l==r) return add(seg[pos].dat,to);
	pd(pos);
	que(to,L,R,lef); que(to,L,R,rgt);//线段树都不会写,逻辑发生巨大错误,upd的时候不管了。。。。写下代码的时候要想想啊!
	pp(pos);
}

void upd(int to,int L,int R,int l,int r,int pos){
	if(L>r||R<l) return;
	if(L<=l&&r<=R) return seg[pos].upd(to);
	pd(pos);
	upd(to,L,R,lef); upd(to,L,R,rgt);
	pp(pos);
}

void dfs(int now){
	if(dp[now]) return;
	dp[now]=1;
	for(auto t:e[now])
		if(t^now)
			dfs(t),dp[now]=max(dp[now],dp[t]+1);
}

void getAns(int now){
	usd[now]=1;
	for(auto t:e[now])
		if(dp[t]+1==dp[now])
			return getAns(t);
}

int main(){
	n=qr(); m=qr();
	for(int t=1;t<=m;++t) data[t].i=qr(),sav[++cnt]=data[t].l=qr(),sav[++cnt]=data[t].r=qr();
	sort(sav+1,sav+cnt+1);
	cnt=unique(sav+1,sav+cnt+1)-sav-1;
	for(int t=1;t<=m;++t) data[t].l=lower_bound(sav+1,sav+cnt+1,data[t].l)-sav,data[t].r=lower_bound(sav+1,sav+cnt+1,data[t].r)-sav;
	sort(data+1,data+m+1);
	for(int t=1;t<=m;++t)
		que(data[t].i,data[t].l,data[t].r,1,cnt,1),upd(data[t].i,data[t].l,data[t].r,1,cnt,1);
	for(int t=1;t<=n;++t) dfs(t);
	int ans=0;
	for(int t=1;t<=n;++t) ans=max(ans,dp[t]);
	printf("%d\n",n-ans);
	for(int t=1;t<=n;++t)
		if(dp[t]==ans)
			getAns(t),t=n;
	for(int t=1;t<=n;++t)
		if(!usd[t])
			printf("%d ",t);
	if(n-ans) putchar('\n');
	return 0;
}


posted @ 2021-08-12 00:49  谁是鸽王  阅读(71)  评论(0编辑  收藏  举报