zoukankan      html  css  js  c++  java
  • CF1601 简要题解

    题面在这里看

    A. Array Elimination

    对每一位考虑,如果有 (m) 个数当前位置为 (1) ((m>0)),那么 (k) 必须是 (m) 的因数,于是直接求出所有位置的 (1) 个数的 (gcd) 即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+10;
    int n,a[N],T;
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d",&n);
    		for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    		int g=0;
    		for(int i=0;i<30;++i){
    			int ct=0;
    			for(int j=1;j<=n;++j)
    				if(a[j]&(1<<i)) ct++;
    			if(ct) g=__gcd(g,ct);
    		}
    		if(!g){
    			for(int i=1;i<=n;++i) printf("%d ",i);puts("");
    			continue;
    		}
    		int i;
    		for(i=1;i*i<=g;++i) if(g%i==0) printf("%d ",i);
    		for(i--;i>=1;--i) if(g%i==0&&(g/i)!=i) printf("%d ",g/i);
    		puts("");
    	}
    	return 0;
    }
    

    B. Frog Traveler

    考虑每次跳跃+下滑后从 (i) 位置到达了 (j) 位置,那么我们一定会选择走 (j-a_j<i-a_i)(j) 位置。于是按 (a_i+i) 从小到大排序,用线段树维护,求出 (dp_i) 后就在线段树上所有 (j+b_j=i)(j) 上记录 (dp_i),那么转移只需要在线段树上找 ([i-a_i,i]) 区间的最小值进行转移即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3e5+10;
    int n,a[N],b[N],f[N],id[N],to[N];
    vector<int> ve[N];
    namespace SGT{
    	#define lc (p<<1)
    	#define rc (p<<1|1)
    	#define mid ((l+r)>>1)
    	int mn[N<<2],pos[N<<2];
    	inline void init(int p=1,int l=0,int r=n){
    		mn[p]=0x3f3f3f3f;pos[p]=0;
    		if(l==r) return ;
    		init(lc,l,mid);init(rc,mid+1,r);
    	}
    	inline void update(int p,int x,int v,int l=0,int r=n){
    		if(l==r){mn[p]=v;pos[p]=l;return ;}
    		x<=mid?update(lc,x,v,l,mid):update(rc,x,v,mid+1,r);
    		mn[p]=min(mn[lc],mn[rc]);
    		pos[p]=mn[p]==mn[lc]?pos[lc]:pos[rc];
    	}
    	inline pair<int,int> query(int p,int ql,int qr,int l=0,int r=n){
    		if(ql<=l&&r<=qr) return make_pair(mn[p],pos[p]);
    		pair<int,int> ans=make_pair(0x3f3f3f3f,0);
    		if(ql<=mid) ans=min(ans,query(lc,ql,qr,l,mid));
    		if(qr>mid) ans=min(ans,query(rc,ql,qr,mid+1,r));
    		return ans;
    	}
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]),id[i]=i;
    	for(int i=1;i<=n;++i) scanf("%d",&b[i]),ve[i+b[i]].push_back(i);
    	sort(id+1,id+n+1,[&](const int &x,const int &y){return x-a[x]<y-a[y];});
    	f[0]=0;
    	SGT::init();
    	SGT::update(1,0,0);
    	for(int i=1;i<=n;++i){
    		int u=id[i];auto p=SGT::query(1,u-a[u],u);
    		f[u]=p.first+1;to[u]=p.second;
    		for(int v:ve[u]) SGT::update(1,v,f[u]);
    	}
    	if(f[n]>=0x3f3f3f3f){
    		puts("-1");
    		return 0;
    	}
    	printf("%d
    ",f[n]);
    	int now=n;
    	while(now!=0){
    		now=to[now];
    		printf("%d ",now);now+=b[now];
    	}
    	return 0;
    }
    

    C. Optimal Insertion

    (b) 排序后进行插入,通过交换法可以容易证明 (b) 插入的位置也是升序的。用线段树维护当前插入每个位置会增加多少逆序对,那么从 (b_i)(b_{i+1}) 就只需要找出所有满足 (a_jin[b_i,b_{i+1}])(j),在线段树上对 ([0,j-1]) 做前缀减,对 ([j,n]) 做后缀加,再查询全局 (min) 即可。这个过程中最优决策点不会左移,因此 (b) 插入的位置也是升序的,内部不会造成影响。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e6+10;
    int T,n,m,a[N],b[N],id[N];
    namespace SGT{
    	#define lc (p<<1)
    	#define rc (p<<1|1)
    	#define mid ((l+r)>>1)
    	int mn[N<<2],tag[N<<2];
    	inline void pushup(int p){mn[p]=min(mn[lc],mn[rc]);}
    	inline void pushdown(int p){
    		if(!tag[p]) return ;
    		mn[lc]+=tag[p];tag[lc]+=tag[p];
    		mn[rc]+=tag[p];tag[rc]+=tag[p];tag[p]=0;
    	}
    	inline void build(int p=1,int l=0,int r=n){
    		tag[p]=0;
    		if(l==r){mn[p]=l;return ;}
    		build(lc,l,mid);build(rc,mid+1,r);
    		pushup(p);
    	}
    	inline void update(int p,int ql,int qr,int v,int l=0,int r=n){
    		if(ql<=l&&r<=qr){mn[p]+=v;tag[p]+=v;return ;}
    		pushdown(p);
    		if(ql<=mid) update(lc,ql,qr,v,l,mid);
    		if(qr>mid) update(rc,ql,qr,v,mid+1,r);
    		pushup(p);
    	}
    }
    namespace BIT{
    	int c[N];
    	inline void init(){memset(c+1,0,sizeof(int)*(n));}
    	inline int lowbit(int x){return x&(-x);}
    	inline void upd(int x){for(;x>0;x-=lowbit(x)) c[x]++;}
    	inline int query(int x){
    		int ret=0;
    		for(;x<=n;x+=lowbit(x)) ret+=c[x];
    		return ret;
    	}
    }
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d%d",&n,&m);
    		BIT::init();SGT::build();
    		for(int i=1;i<=n;++i) scanf("%d",&a[i]),id[i]=i;
    		for(int i=1;i<=m;++i) scanf("%d",&b[i]);
    		sort(b+1,b+m+1);
    		sort(id+1,id+n+1,[&](const int &x,const int &y){return a[x]<a[y];});
    		int now=1;
    		long long ans=0;
    		for(int l=1,r=0;l<=n;l=r+1){
    			r=l;
    			while(r<n&&a[id[r+1]]==a[id[r]]) ++r;
    			for(int i=l;i<=r;++i) ans+=BIT::query(id[i]);
    			for(int i=l;i<=r;++i) BIT::upd(id[i]);
    		}
    		for(int i=1,las=0;i<=m;++i){
    			if(i!=1&&b[i]==b[i-1]){ans+=las;continue;}
    			while(now<=n&&a[id[now]]<b[i]) SGT::update(1,0,id[now]-1,1),SGT::update(1,id[now],n,-1),now++;
    			int ql=now;
    			while(ql<=n&&a[id[ql]]==b[i]) ++ql;
    			for(int j=now;j<ql;++j) SGT::update(1,id[j],n,-1);
    			las=SGT::mn[1];ans+=las; 
    			for(int j=now;j<ql;++j) SGT::update(1,0,id[j]-1,1);
    			now=ql;
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    D. Difficult Mountain

    神仙结论题:将所有人按 (max(a,s)) 为第一关键字,(s) 为第二关键字后排序,然后依次考虑每一个人,能选就选。

    证明:对 (ale s)(a>s) 的人分类讨论,不妨设正在比较 (i,j)

    • (a_ile s_i,a_jle s_j),他们都不会影响山的高度,于是 (s) 的人先攀登一定更优。
    • (a_i>s_i,a_j>s_j),不妨设 (a_i<a_j),若 (j) 先上山则 (i) 一定不可能上山了,因此不如先尝试 (i)
    • (a_ile s_i,a_j>s_j,s_i<a_j),和上一个一样,(j) 上山后 (i) 一定上不了,不如让 (i) 先尝试。
    • (a_ile s_i,a_j>s_j,s_i>a_j),先让 (j) 上山不会影响 (i),因此先尝试 (j) 一定不劣。
    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e5+10;
    int n,ans,d;
    struct node{
    	int s,d;
    }a[N];
    int main(){
    	scanf("%d%d",&n,&d);
    	for(int i=1;i<=n;++i)
    		scanf("%d%d",&a[i].s,&a[i].d);
    	sort(a+1,a+n+1,[&](const node &x,const node &y){
    		int t1=max(x.s,x.d),t2=max(y.s,y.d);
    		if(t1^t2) return t1<t2;
    		if(x.s^y.s) return x.s<y.s;
    		return x.d<y.d;
    	});
    	ans=0;
    	for(int i=1;i<=n;++i) if(a[i].s>=d) ans++,d=max(d,a[i].d);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    E - Phys Ed Online

    首先可以用单调队列求出 (mn_i=min_{jin[i,i+k]}a_j),于是

    [ans(l,r)=a_l+sum_{j=1}^{l+kjle r}min_{0le w< j}mn_{l+wk} ]

    于是我们可以按照 (mod k) 的余数分类,将问题转化为 (k=1) 的情况。

    从右到左考虑每个左端点,对当前点的答案做出贡献的一定只有一个单减的序列,使用单调栈维护这一单减序列,预处理倍增数组 (f_{i,j},g_{i,j}) 表示从 (i) 出发的单调栈的第 (2^j) 项,以及从 (i) 跳到 (f_{i,j}) 途中所有数的贡献,然后就可以 (mathcal O(nlog n+qlog n)) 倍增求出答案了。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=6e5+10;
    int mn[N],to[N][20],n,q,k,a[N],pw[N],b[N],l,r;
    ll s[N][20];
    int main(){
    	scanf("%d%d%d",&n,&q,&k);
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    	l=1;r=0;
    	pw[0]=k;
    	for(int i=1;i<20&&pw[i-1]<=n;++i) pw[i]=pw[i-1]<<1;
    	for(int i=n;i>=1;--i){
    		while(l<=r&&a[b[r]]>a[i]) --r;
    		b[++r]=i;
    		while(l<=r&&b[l]>i+k) ++l;
    		mn[i]=a[b[l]];
    	}
    	a[n+k]=-1;
    	for(int i=n+k;i>n-k;--i) for(int j=0;j<20;++j) to[i][j]=n+k; 
    	for(int i=n-k;i>=1;--i){
    		to[i][0]=i+k;
    		if(mn[to[i][0]]>=mn[i]){
    			int x=to[i][0];
    			for(int j=19;j>=0;--j)
    				if(mn[to[x][j]]>=mn[i]) x=to[x][j];
    			to[i][0]=to[x][0];
    		}
    		s[i][0]=1ll*mn[i]*(to[i][0]-i)/k;
    		for(int j=1;j<20;++j){
    			to[i][j]=to[to[i][j-1]][j-1];
    			s[i][j]=s[i][j-1]+s[to[i][j-1]][j-1];
    		}
    	}
    	while(q--){
    		int l,r;scanf("%d%d",&l,&r);
    		r-=(r-l)%k;
    		ll ans=a[l]; 
    		for(int i=19;i>=0;--i)
    			if(to[l][i]<=r) ans+=s[l][i],l=to[l][i];
    		printf("%lld
    ",ans+1ll*mn[l]*((r-l)/k));
    	}
    	return 0;
    }
    

    F. Two Sorts

    又一道神仙题。咕咕咕

  • 相关阅读:
    如何导出API文档,没有你做不到,只有你想不到!
    Oracle 数据库数据排名函数:rank() 和dense_rank() 。
    第二次绩效评估
    检查博客
    “E记”Alpha版使用说明
    绩效评估
    第一个冲刺周期意见评论
    第一个冲刺周期意见汇总
    第一次冲刺会议评审总结
    软件工程概论第一个冲刺周期
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/15473083.html
Copyright © 2011-2022 走看看