CF1470 题解

CF1470A

考虑贪心一下,将 \(k\) 倒序排序,然后给大的 \(k\) 配尽可能小的礼物即可。

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=1e6+9,mod=1e9+7;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int T,n,m,c[N],k[N];

signed main() {
	T=read();
	while(T--) {
		n=read(), m=read();
		rep(i,1,n) k[i]=read();
		rep(i,1,m) c[i]=read();
		sort(k+1,k+n+1); reverse(k+1,k+n+1);
		int pos=1,ans=0;
		rep(i,1,n) {
			if(pos<=m&&pos<=k[i]) {
				ans+=c[pos++];
			} else {
				ans+=c[k[i]];
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

CF1570B

这个条件相当于两个数相乘等于平方数,相当于两个数去除平方因子后相等。

我们定义一群相同的数为一个块。容易发现,在 \(2\) 秒后就不会发生块的“合并”。第一秒的时候,相等的数全部合并起来。第二秒的时候,大小为偶数的块和 \(1\) 合并起来。然后算一下即可。

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=1e6+9,mod=1e9+7;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int t,n,q,a[N],pr[N],cnt;
bool vst[N];

void sieve(int n){
	for(int i=2;i<=n;i++){
		if(!vst[i]) pr[++cnt]=i;
		for(int j=1;j<=cnt&&i*pr[j]<=n;j++){
			vst[i*pr[j]]=1;
			if(i%pr[j]==0) break;
		}
	}
}

signed main() {
	t=read();
	sieve(1000000);
	while(t--) {
		n=read();
		rep(i,1,n) {
			a[i]=read();
			for(int j=1;j<=cnt&&pr[j]*pr[j]<=a[i];j++) {
				while(a[i]%(pr[j]*pr[j])==0) a[i]/=(pr[j]*pr[j]);
			}
		}
		sort(a+1,a+n+1); a[n+1]=0;
		int mx=0,sum=0,cnt=0,cnt1=0;
		rep(i,1,n+1) {
			if(a[i]!=a[i-1]) {
				mx=max(mx,cnt);
				if(cnt%2==0) sum+=cnt;
				if(a[i-1]==1&&cnt%2!=0) cnt1=cnt;
				cnt=1;
			} else cnt++;
		}
		q=read();
		while(q--) {
			int w=read();
			if(w==0) printf("%lld\n",mx);
			else printf("%lld\n",max(mx,sum+cnt1));
		}
	}
	return 0;
}

CF1570C

一些很妙的观察。我们发现实际上,\(p+1,p+2,...,n,1,...,p-1\) 所持有的数量是单调不升的,所以我们可以在确定了一个 \(p+x\) 后通过二分来找到。又有一个发现,对于前 \(n/2\) 论,每轮都会使得拥有 \(> k\) 张牌的人多至少一个,意味着在 \(\sqrt n\) 轮后,必有一段 \(\sqrt n\) 的连续区间使得其都 \(>k\)。我们把序列分块,每块中挑一个数来问,必然能问到一个 \(>k\) 的,然后二分即可。为了避免麻烦,\(n<...\) 的时候设块长为 1 暴力即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int n,k,bs;

int qry(int x) {
	printf("? %d\n",(x-1)%n+1); fflush(stdout);
	return read();
}

int main() {
	n=read(), k=read();
	bs=(n>900?400:1);
	rep(i,1,bs) qry(1);
	int l=0,r=0;
	for(int i=1;i<=n;i+=bs) {
		int p=qry(i); if(p==k) continue;
		if(p>k) r=i; else l=i;
	} r+=n*(l>r);
	while(l<=r) {
		int mid=(l+r)/2, p=qry(mid);
		if(p==k) {
			printf("! %d",(mid-1)%n+1);
			return 0;
		}
		if(p>k) r=mid-1;
		else l=mid+1;
	}
	throw "shit";
	return 0;
}

CF1470D

不连通无解。然后考虑证明其他情况都有解。一个非常有意思的证明,就是归纳证明,在证明的同时也能搞出解法。

考虑 \(dfs\) 序。假设目前 \(n-1\) 的图有解,考虑加入一个非割点(可以按照 \(dfs\) 序加入来保证),如果它已经和选择的点有连边,那么自然可行;否则我们在这个点上放老师即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=3e5+9;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int T,n,m,pos[N],dfn[N],tick,tag[N];
vector<int>e[N];

void dfs(int u) {
	dfn[u]=++tick,pos[tick]=u;
	for(auto v:e[u]) if(!dfn[v]) dfs(v);
}

int main() {
	T=read();
	while(T--) {
		n=read(), m=read();
		rep(i,1,n) tag[i]=dfn[i]=0, e[i].clear();
		tick=0;
		rep(i,1,m) {
			int u=read(), v=read();
			e[u].push_back(v), e[v].push_back(u);
		}
		dfs(1);
		bool flag=0;
		rep(i,1,n) if(!dfn[i]) {flag=1; break;}
		if(flag) {puts("NO"); continue;}
		puts("YES");
		vector<int>ans;
		rep(i,1,n) {
			int u=pos[i];
			tag[u]=1;
			for(auto v:e[u]) tag[u]&=!tag[v];
			if(tag[u]) ans.push_back(u);
		}
		printf("%d\n",(int)ans.size());
		for(auto x:ans) printf("%d ",x);
		puts("");
	}
	return 0;
}

CF1470E

非常的妙啊。首先一种翻转唯一对应一种最终结果的数列,所以最终方案数 \(p(n,c)=\sum_{i=0}^{c}\binom{n-1}{i}\)

考虑设一个 \(L(i,c)\) 表示考虑 \([i,c]\) 后缀用花费不超过 \(c\) 的代价得到的最终序列按字典序排序时,所作的第一次翻转操作,的序列。序列中每个元素可以写成 \(l,r,p\) 的形式,代表:翻转区间左端点,翻转区间右端,这种东西对应的情况数。

\(F(i,c,k)\) 表示 \(L(i,c)\) 的第 \(k\) 大的方案。这个可以通过二分解决,即二分 \(pos\) 使得 \(w_1+...+w_{pos}\ge k\)

\(L\) 的求法很秒。考虑从 \(L(i+1)\) 转移到 \(L(i)\)。首先 \(i\)\(c\) 种翻法。每个 \(L\) 维护一个双端队列,考虑翻 \([i,i+j]\) 时的情况,发现要么被放到队列左边,要么放到右边,取决于 \(a_{i+j}\)\(a_i\) 相比的大小。

然后问询最多调用 \(c\)\(F\),也就是说复杂度 \(O(nc^2+qc\log(nc))\)

#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=3e4+9;
typedef pair<int,int>pii;

inline long long read() {
	long long res=0, w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) {res=res*10+c-48; c=getchar();}
	return res*w;
}

int T,n,c,q,a[N];

int C(int x,int y,int ret=1) {
	if(x<=1) return !y;
	if(x-1<y) return 0;
	per(i,x-1,x-y) ret*=i;
	rep(i,1,y) ret/=i;
	return ret;
}
int calc(int len,int c,int ret=0) {rep(i,0,c) ret=(ret+C(len,i)); return ret;}

int ql[9],qr[9],s[6][N],sw[6][N*10];
struct node {
	int l,r,w;
	node(int _l=0,int _r=0,int _w=0) {l=_l,r=_r,w=_w;}
} dq[6][N*10];
bool cmp(const node &x,const node &y) {return a[x.r]<a[y.r];}

node F(int p,int c,int k) {
	int lst=s[c][p];
	int l=lst+1,r=qr[c]-ql[c]+2;
	while(l<r) {
		int mid=(l+r)/2;
		if(sw[c][mid]-sw[c][lst]>=k) r=mid;
		else l=mid+1;
	}
	return node(dq[c][ql[c]+l-1].l,dq[c][ql[c]+l-1].r,sw[c][l-1]-sw[c][lst]);
}

signed main() {
	T=read();
	while(T--) {
		n=read(), c=read(), q=read();
		int lim=calc(n,c);
		rep(i,1,n) a[i]=read();
		rep(k,1,c) {
			ql[k]=12002,qr[k]=12001; rep(i,1,n) s[k][i]=0;
			dq[k][++qr[k]]=node(n,n,1);
			per(i,n-1,1) {
				int dl=0,dr=0;
				rep(j,1,min(k,n-i)) {
					if(a[i+j]<a[i]) dq[k][ql[k]-(++dl)]=node(i,i+j,calc(n-i-j,k-j)), s[k][i+1]++;
					else dq[k][qr[k]+(++dr)]=node(i,i+j,calc(n-i-j,k-j));
				}
				if(dl) sort(dq[k]+ql[k]-dl,dq[k]+ql[k],cmp), ql[k]-=dl;
				if(dr) sort(dq[k]+qr[k]+1,dq[k]+qr[k]+dr+1,cmp), qr[k]+=dr;
			}
			rep(i,1,n) s[k][i]=s[k][i-1]+s[k][i];
			rep(i,ql[k],qr[k]) sw[k][i-ql[k]+1]=sw[k][i-ql[k]]+dq[k][i].w;
		}
		while(q--) {
			int pos=read(), k=read();
			if(k>lim) {puts("-1"); continue;}
			vector<pii>v;
			int p=1,rem=c,ok=k;
			while(rem&&p<=n) {
				node tmp=F(p,rem,k);
				v.push_back(pii(tmp.l,tmp.r));
				k-=tmp.w, rem-=(tmp.r-tmp.l), p=tmp.r+1;
			}
			bool flag=1;
			for(auto pp:v) {
				if(pp.fi<=pos&&pp.se>=pos) {
					flag=0;
					printf("%lld\n",a[pp.se+pp.fi-pos]);
					break;
				}
			} if(flag) printf("%lld\n",a[pos]);
		}
	}
	return 0;
}
posted @ 2021-08-26 20:11  LarsWerner  阅读(59)  评论(0编辑  收藏  举报