CF1558 简要题解

A. Charmed by the Game

观察容易猜到大概是最大值和最小值中间几乎都取得到,分奇偶性讨论即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int mod=1e9+7;
#define inf 1e9
inline 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-'0';c=getchar();}
	return x*f;
}
int T,n,m;
int main(){
	T=read();
	while(T--){
		n=read(),m=read();
		if(n<m)swap(n,m);
		int S=n+m,hf=(S+1)/2;
		int Mn=n-hf;
		if(S&1){
			int Mx=S-Mn;
			printf("%d\n",Mx-Mn+1);
			for(int i=Mn;i<=Mx;i++)
				printf("%d ",i);puts("");
		}else{
			int Mx=S-Mn;
			printf("%d\n",(Mx-Mn)/2+1);
			for(int i=Mn;i<=Mx;i+=2)
				printf("%d ",i);puts("");
		}
	}
	return 0;
}

B. Up the Strip

考虑减法操作,就是一个后缀和。

考虑除法操作,我们枚举除数,不难发现也是一段,于是同样后缀和优化。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e7+10;
#define inf 1e9
inline 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-'0';c=getchar();}
	return x*f;
}
int n,mod,f[maxn],S[maxn];
int main(){
	n=read(),mod=read();f[n]=S[n]=1;
	for(int i=n-1;i>=1;i--){
		f[i]=S[i+1];
		for(int j=2;i*j<=n;j++){
			f[i]=(f[i]+S[i*j])%mod;
			f[i]=(f[i]-S[(i+1)*j]+mod)%mod;
		}S[i]=(S[i+1]+f[i])%mod;
	}printf("%d\n",(f[1]+mod)%mod);
	return 0;
}

C. Bottom-Tier Reversals

这种题一看就是用 \(5\) 次操作消掉一对。分两种情况讨论:

  • \(p_{n-1}<p_n\),操作 \(p_n\)\(n\) 转到第一个,然后把 \(n\) 贴到 \(n-1\) 前面,再一次操作把 \(n\) 转到 \(n-1\) 后面,再把 \(n\)\(n-1\) 转到 \(1\)\(2\),然后转到 \(n\)\(n-1\)

  • \(p_{n-1}>p_n\),一模一样的想法,没有任何区别。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int mod=1e9+7;
#define inf 1e9
inline 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-'0';c=getchar();}
	return x*f;
}
const int N=2505;
int T,n,m,a[N],flg;
#define pb push_back
vector<int>Ans;
inline void rev(int len){
	Ans.pb(len);
	for(int i=1;i<=len/2;i++)swap(a[i],a[len+1-i]);
}
inline void work(int len){
	if(len==1)return;
	int p1,p2;
	for(int i=1;i<=n;i++)
		if(a[i]==len-1)p1=i;
		else if(a[i]==len)p2=i;
	if(p1>p2){
	rev(p2);rev(p1-1);
	rev(p1+1);rev(3);rev(len);
	}else{
		rev(p2);p1=1+p2-p1;
		rev(p1-1);rev(p1+1);
		rev(3);rev(len);
	}work(len-2);
}
inline void solve(){
	n=read();flg=0;
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)
		flg|=(abs(i-a[i])&1);
	if(flg)return void(puts("-1"));
	Ans.clear();work(n);
//	if(flg)return void(puts("-1"));
	printf("%d\n",Ans.size());
	for(auto x:Ans)
		printf("%d ",x);puts("");
//	for(int i=1;i<=n;i++)
//		printf("%d ",a[i]);puts("");
}
int main(){
	T=read();
	while(T--)solve();
	return 0;
}

D. Top-Notch Insertions

你想想,这种题的难点怎么可能再计数上,肯定是个数据结构吧。

我们不妨按照输入把最终的序列推演出来,考察其差分,发现有些地方必须要加一,我们只要算出有多少这种然后就是一个插板。

发现这种就是这俩始终挨着,一旦被插队就失效了。平衡树维护即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int mod=998244353;
#define inf 1e9
inline 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-'0';c=getchar();}
	return x*f;
}
int T,n,m,P[maxn],inv[maxn],fac[maxn],ifc[maxn];
inline int com(int x,int y){
	return 1ll*fac[x]*ifc[y]%mod*ifc[x-y]%mod;
}
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
unsigned int aaa=192608217;
inline unsigned int rd(){
	aaa^=aaa>>15;aaa+=aaa<<12;aaa^=aaa>>3;
	return aaa;
}
struct fhq{
	int lc,rc,sz,laz,val,tag;
	unsigned int rnd;
	inline void init(int x){
		lc=rc=laz=0;sz=1;
		val=x;rnd=rd();tag=1;
	}
}tr[maxn];int cnt,a[maxn],tot,vis[maxn];
inline void upd(int x){
	tr[x].sz=1+tr[tr[x].lc].sz+tr[tr[x].rc].sz;
}
inline void pushup(int x,int y){
	if(!x)return;
	tr[x].laz+=y;tr[x].val+=y;
}
inline void pushdown(int x){
	if(!tr[x].laz)return;
	pushup(tr[x].lc,tr[x].laz);
	pushup(tr[x].rc,tr[x].laz);
	tr[x].laz=0;
}
inline void split(int x,int &l,int &r,int k){
	if(!x)return void(l=r=0);pushdown(x);
//	if(tr[tr[x].lc].sz>=k)r=x,split(tr[x].lc,l,tr[x].lc,k);
//	else l=x,split(tr[x].rc,tr[x].rc,r,k-1-tr[tr[x].lc].sz);
	if(tr[x].val<=k)l=x,split(tr[x].rc,tr[l].rc,r,k);
	else r=x,split(tr[x].lc,l,tr[r].lc,k);
	upd(x);
}
inline int merge(int x,int y){
	if(!x||!y)return x+y;
	pushdown(x),pushdown(y);
	if(tr[x].rnd>tr[y].rnd){
		tr[x].rc=merge(tr[x].rc,y);
		upd(x);return x;
	}else{
		tr[y].lc=merge(x,tr[y].lc);
		upd(y);return y;
	}
}
//inline void dfs(int x){
//	if(!x)return;pushdown(x);
//	dfs(tr[x].lc);
//	printf("%d %d\n",tr[x].val.fi,-tr[x].val.se);
//	dfs(tr[x].rc);
//}
inline void solve(){
	n=read(),m=read();int rt,ans=0,tot=0;
	for(int i=1,x,y;i<=m;i++){
		x=read(),y=read();
		tr[i].init(y);
		if(i==1)rt=1;
		else{
			int lc,rc,id;
			split(rt,lc,rc,y-1);id=lc;
			while(tr[id].rc)id=tr[id].rc;
			if(tr[id].tag&&tr[id].val==y-1)++ans,tr[id].tag=0;
			pushup(rc,1);
			rt=merge(lc,merge(i,rc));
		}
	}//printf("ans=%d\n",ans);
//	dfs(rt);puts("");
	printf("%d\n",com(n+n-m-1+ans,n));
	for(int i=1;i<=m;i++)tr[i].laz=0;
}
int main(){
	inv[1]=fac[0]=ifc[0]=1;
	for(int i=2;i<maxn;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<maxn;i++)fac[i]=1ll*fac[i-1]*i%mod;
	for(int i=1;i<maxn;i++)ifc[i]=1ll*ifc[i-1]*inv[i]%mod;
	T=read();while(T--)solve();
	return 0;
}

E. Down Below

luogu blog link

在没有看到不能返回之前我认为这是个简单题。

然而有这一个条件,显然我们每次只能拓展类似于一个环的东西。

首先肯定二分答案,然后我们发现如果能够遍历,那么你每次瞎拓展都行。

也就是说,你随意找环,最后一定能遍历,如果遍历不了,那就是遍历不了。

考虑暴力搜索找环,如果走到了之前走过的点(不同起点也算)那么我们一定可以走回去。所以每次找环每个点只会被经过一次。

注意此处我们其实是无限弱化了条件,但是达到了想要的效果。

所以复杂度为 \(O(n(n+m)\log V)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int mod=1e9+7;
#define inf 1e9
inline 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-'0';c=getchar();}
	return x*f;
}
const int N=1005;
#define pb push_back
#define ll long long
vector<int>G[N];
int n,m,T,a[N],b[N],vis[N],flg,pre[N];
inline void tag(int x){for(;x;x=pre[x])vis[x]=1;}
inline void dfs(int x,int las,ll v){
	for(auto t:G[x]){
		if((vis[x]&&vis[t])||(a[t]>=v)||(t==las))continue;
		if(vis[t]){flg=1;tag(x);return;}
		if(pre[t]){flg=1;tag(x),tag(t);return;}
		pre[t]=x;dfs(t,x,v+b[t]);if(flg)return;
	}
}
inline bool chk(int val){
	for(int i=1;i<=n;i++)vis[i]=0;
	vis[1]=1;while(1){
		int cnt=0;ll v=val;
		for(int i=1;i<=n;i++){
			pre[i]=0;
			if(vis[i])++cnt,v+=b[i];
		}if(cnt==n)return 1;flg=0;
		for(int i=1;i<=n&&!flg;i++)
			if(vis[i])dfs(i,0,v);
		if(!flg)return 0;
	}
}
inline void solve(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)G[i].clear();
	for(int i=2;i<=n;i++)a[i]=read();
	for(int i=2;i<=n;i++)b[i]=read();
	for(int i=1,x,y;i<=m;i++)
		x=read(),y=read(),G[x].pb(y),G[y].pb(x);
	int l=1,r=inf+1,ans=r;
	while(l<=r){
		int mid=(l+r)>>1;
		if(chk(mid))ans=mid,r=mid-1;
		else l=mid+1;
	}printf("%d\n",ans);
}
int main(){
	T=read();
	while(T--)solve();
	return 0;
}

F. Strange Sort

luogu blog link

考虑转化为 \(01\) 排序,答案即为所有划分值答案的最大值。

容易发现在 \(01\) 排序中,相同数字的相对位置不会变化,故答案即为最右侧的 \(0\) 的就位时间。

\(f_i\) 为第 \(i\)\(0\) 归为的时间,\(k_i\) 为其前面 \(1\) 的个数,\(p_i\) 为其位置。

  • \(p_i=i\),则 \(f_i=0\),这显然是一段前缀。

  • 否则有 \(f_i=\max\{f_{i-1}+1,k_i+(p_i\bmod 2)\}\)

\(f_i=\max\limits_{j\leqslant i,p_j\neq j}\{ k_j+(p_j\bmod 2)+i-j\}\),维护即可。

每一步都很自然,实现也很简单,但为什么我不会呢?

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int mod=1e9+7;
#define inf 1e9
inline 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-'0';c=getchar();}
	return x*f;
}
int n,m,a[maxn],T,p[maxn],bt[maxn],tr[maxn<<2],laz[maxn<<2];
inline void build(int h,int l,int r){
	tr[h]=-inf;laz[h]=0;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(h<<1,l,mid);
	build(h<<1|1,mid+1,r);
}
inline void pushup(int h,int z){tr[h]+=z;laz[h]+=z;}
inline void pushdown(int h){pushup(h<<1,laz[h]);pushup(h<<1|1,laz[h]);laz[h]=0;}
inline void update(int h,int l,int r,int x,int y){
	if(l==r)return void(tr[h]=y);
	int mid=(l+r)>>1;pushdown(h);
	if(mid>=x)update(h<<1,l,mid,x,y);
	else update(h<<1|1,mid+1,r,x,y);
	tr[h]=max(tr[h<<1],tr[h<<1|1]);
}
inline void modify(int h,int l,int r,int x,int y,int z){
	if(l>y||r<x||x>y)return;
	if(l>=x&&r<=y)return void(pushup(h,z));
	int mid=(l+r)>>1;pushdown(h);
	modify(h<<1,l,mid,x,y,z);
	modify(h<<1|1,mid+1,r,x,y,z);
	tr[h]=max(tr[h<<1],tr[h<<1|1]);
}
inline int query(int h,int l,int r,int x,int y){
	if(l>y||r<x)return -inf;
	if(l>=x&&r<=y)return tr[h];
	int mid=(l+r)>>1;pushdown(h);
	return max(query(h<<1,l,mid,x,y),query(h<<1|1,mid+1,r,x,y));
}
inline void bl(int h,int l,int r){
	if(l==r){
		printf("%d ",tr[h]);
		return;
	}int mid=(l+r)>>1;
	pushdown(h);
	bl(h<<1,l,mid),bl(h<<1|1,mid+1,r);
}
inline void solve(){
	n=read();int ans=0;
	for(int i=1;i<=n;i++)
		a[i]=read(),p[a[i]]=i,bt[i]=0;
	build(1,1,n);
	for(int i=1,pos=1;i<n;i++){
		for(int j=p[i];j<=n;j+=j&(-j))bt[j]++;
		int K=p[i],P=p[i],V=0;
		for(int j=P;j;j-=j&(-j))K-=bt[j];
		while(pos<=n&&a[pos]<=i)++pos;
		if(pos==i+1)continue;
		update(1,1,n,P,K+(P&1)-(P-K));
		modify(1,1,n,P+1,n,-2);
//		printf("i=%d P=%d K=%d res=%d pos=%d\n",i,P,K,i+query(1,1,n,pos,n),pos);
//		bl(1,1,n);puts("");
		ans=max(ans,i+query(1,1,n,pos,n));
	}printf("%d\n",ans);
}
int main(){
	T=read();
	while(T--)solve();
	return 0;
}
posted @ 2022-11-04 11:00  syzf2222  阅读(70)  评论(0编辑  收藏  举报