• 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
    ",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
    ",mx);
    			else printf("%lld
    ",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
    ",(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
    ",(int)ans.size());
    		for(auto x:ans) printf("%d ",x);
    		puts("");
    	}
    	return 0;
    }
    

    CF1470E

    非常的妙啊。首先一种翻转唯一对应一种最终结果的数列,所以最终方案数 (p(n,c)=sum_{i=0}^{c}inom{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+qclog(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
    ",a[pp.se+pp.fi-pos]);
    					break;
    				}
    			} if(flag) printf("%lld
    ",a[pos]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    143. Reorder List
    圆圈中最后剩下的数
    求1+2+3+...+n
    不用加减乘除做加法
    构建乘积数组
    199. Binary Tree Right Side View
    把字符串转换成整数
    java stream
    物流运费的维护架构
    9、定义类与方法
  • 原文地址:https://www.cnblogs.com/TetrisCandy/p/15191361.html
Copyright © 2020-2023  润新知