AtCoder Beginner Contest 282 题解(E-Ex)

AtCoder Beginner Contest 282 题解(E-Ex)

下半篇章。

ABC282E Choose Two and Eat One(Diff. 1,154)

Solution:

对所有点之间里边,跑一个最大生成树即可。

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
struct edge{
	int u,v,w;
}g[500010];
bool cmp(edge a,edge b){
	return a.w>b.w;
}
int mod;
int ksm(int a,int b)
{
	int ans=1;
  	a%=mod;
  	for(;b;b>>=1){
    	if(b&1) ans=(a*ans)%mod;
    	a=(a*a)%mod;
  	}
	return ans;
}
int fa[500010];
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
int idx;
int a[600];
int kruskal(){
	int ans=0;
	for(int i=1;i<=idx;i++){
		int u=find(g[i].u);
		int v=find(g[i].v);
		if(u!=v){
			fa[u]=v;
			ans+=g[i].w;
		}
	}
	return ans;
}
signed main(){
	for(int i=1;i<=500000;i++) fa[i]=i;
	int n;
	cin>>n;
	cin>>mod;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			g[++idx]=edge{i,j,(ksm(a[i],a[j])+ksm(a[j],a[i]))%mod};
		}
	}
	sort(g+1,g+idx+1,cmp);
	cout<<kruskal();
}

ABC282F Union of Two Sets (Diff. 1,604)

Solution:

显然每个端点都得有开始的线段

很容易想到回答询问的时候大概是左边一个区间,右边一个区间拼起来。

很想 ST 表的结构,我们倍增构造,每一个 \(st[i][j]\) 表示以 \(i\) 为开始,\(2^j\) 为长度,安排一个线段。

查询也是类似于 ST 表。

考虑线段条数的限制。

\(n=4,000\) 时,打表可得线段条数为 ,满足条件。

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int st[4010][13];
int lg[4010],cnt;
void query(int x,int y){
	int lga=lg[y-x+1];
	cout<<st[x][lga]<<" "<<st[y-(1<<lga)+1][lga]<<endl;
	cout.flush();
}
int n;
int ql[50010],qr[50010];
signed main(){
	cin>>n;
	for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
	for(int j=0;(1<<j)<=n;j++){
		for(int i=1;i+(1<<j)-1<=n;i++){
			st[i][j]=++cnt;
			ql[cnt]=i,qr[cnt]=i+(1<<j)-1;
		}
	}
	cout<<cnt<<endl;
	cout.flush();
	for(int i=1;i<=cnt;i++){
		cout<<ql[i]<<" "<<qr[i]<<endl;
		cout.flush();
	}
	int q;
	cin>>q;
	for(int i=1;i<=q;i++){
		int l,r;
		cin>>l>>r;
		query(l,r);
	}
	return 0;
}

ABC282G Similar Permutation (Diff.1734)

Solution:

\(dp[i][j][a][b]\) 表示当前枚举到第 \(i\) 位,相似度为 \(j\) ,第 \(i\) 位两个数在对应数组中排名为 \(a,b\) 的方案数。

二位前缀和优化 dp 即可。

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int s[110][110],dp[2][110][110][110];
signed main(){
	int n,k,p;
	cin>>n>>k>>p;
	int cur=1; 
	for(int a=1;a<=n;a++){
		for(int b=1;b<=n;b++){
			dp[cur][0][a][b]=1;
		}
	}
	for(int i=2;i<=n;i++){
		cur^=1;
		memset(dp[cur],0,sizeof dp[cur]);
		for(int j=0;j<i;j++){
			int mx=n-i+2;
			for(int a=1;a<=mx;a++){
				for(int b=1;b<=mx;b++){
					s[a][b]=(s[a-1][b]+s[a][b-1]-s[a-1][b-1]+dp[cur^1][j][a][b])%p;
					s[a][b]=(s[a][b]+p)%p;
				}
			}
			for(int a=1;a<mx;a++){
				for(int b=1;b<mx;b++){
					dp[cur][j][a][b]=(dp[cur][j][a][b]+s[a][mx]-s[a][b]+s[mx][b]-s[a][b])%p;
					dp[cur][j][a][b]=(dp[cur][j][a][b]+p)%p;
					//cout<<i<<" "<<cur<<" "<<j<<" "<<a<<" "<<b<<" "<<dp[cur][j][a][b]<<endl;
				}
			}
			for(int a=1;a<mx;a++){
				for(int b=1;b<mx;b++){
					dp[cur][j+1][a][b]=(dp[cur][j+1][a][b]+s[a][b]+s[mx][mx]-s[a][mx]-s[mx][b]+s[a][b])%p;
					dp[cur][j+1][a][b]=(dp[cur][j+1][a][b]+p)%p;
				}
			}
		}
	}
	cout<<dp[cur][k][1][1]<<endl;
}

ABC282Ex Min + Sum(Diff. 2,412)

Solution:

看到寻找符合条件的区间,不难想到使用分治解决。

按照套路寻找左端点,找有哪些右端点符合条件。

设区间为 \([l,r]\),当前枚举的左端点为 \(lpos\),区间 \([l,mid]\) 的最小值是 \(lmin\),那么所有的右端点 \(i\) 可以按照 \([mid+1,i]\) 的最小值 \(rmin_i\)\(lmin\) 的大小分成两类。

由于 \(rmin_i\) 单调非严格递减,且所以正好是左右两段。

  1. \(rmin_i\ge lmin\) :这类情况下,区间 \([l,i]\) 的值为区间和加上 \(lmin\),由于区间和也是单调递增的,所以我们可以二分解决这一段。

  2. \(rmin_i<lmin\):这类情况下区间的值不再具有单调性,我们对 \(b\) 数组求一个前缀和,那么区间和可以表示成 \(-pfs[lpos-1]+pfs[i]+rmin_i)\)。我们将与有半部分有关的提取出来,那么剩下的是 \(pfs[lpos-1]\) ,满足单调递减性质,所以我们可以把所有右端点存入一个数据结构里面,每此更新左端点时把所有已经不满足限制的删去即可。

    不满足限制的情况有两种,一种是当前点在更新左端点后不属于第二类了,另外一种是在算答案贡献的时候已经不满足 \(s\) 的限制了,后面也不可能满足,删去。

    查询则查询值小于等于阈值 \(s-pfs[lpos-1]\) 的个数即可。

    不难想到可以用 std::set 维护。

时间复杂度 \(O(n\log^2n)\)

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int n,m,a[300010],b[300010],ans;
int qzamin[300010],pfs[300010];
#define pr pair<int,int>
#define mp make_pair
void solve(int l,int r){
	if(l==r){
		ans+=((a[l]+b[l])<=m);
		return;
	}
	int mid=(l+r)>>1;
	solve(l,mid);
	solve(mid+1,r);
	int lemin=1e18,rxend=mid;
	qzamin[mid]=1e18;
	for(int i=mid+1;i<=r;i++) qzamin[i]=min(qzamin[i-1],a[i]);
	set<pr> s1;
	for(int i=mid+1;i<=r;i++) s1.insert(mp(-qzamin[i]-pfs[i]+pfs[mid],i));
	for(int lpos=mid;lpos>=l;lpos--){
		lemin=min(lemin,a[lpos]);	
		while(rxend<r&&qzamin[rxend+1]>=lemin){
			rxend++;
			auto it=s1.find(mp(-qzamin[rxend]-pfs[rxend]+pfs[mid],rxend));
			if(it!=s1.end()) s1.erase(it);
		}
		int ql=mid+1,qr=rxend,cur=mid;
		while(ql<=qr){
			int md=(ql+qr)>>1;
			if(pfs[md]-pfs[lpos-1]+lemin<=m){
				cur=md,ql=md+1;
			}else qr=md-1;
		}
		ans+=cur-mid;
		while(!s1.empty()){
			pr x=*s1.begin();
			if((-x.first+pfs[mid]-pfs[lpos-1])<=m){
				break;
			}
			s1.erase(s1.begin()); 
		}
		ans+=s1.size();
	}	
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) b[i]=read(),pfs[i]=pfs[i-1]+b[i];
	solve(1,n);
	printf("%lld\n",ans);
	return 0;
}

posted @ 2023-11-08 21:58  ArizonaYYDS  阅读(60)  评论(0)    收藏  举报