AtCoder Grand Contest 013

本文探讨了多种算法和数据结构的应用,包括贪心算法、深度优先搜索、矩阵快速幂等,并展示了它们在不同问题中的解决方案,如排序、路径寻找和环上蚂蚁位置计算等。此外,还涉及了堆叠立方体、两面卡片排列和正方形放置的策略分析。
摘要由CSDN通过智能技术生成

A - Sorted Arrays

贪心即可,从前往后看看能不能加入前一个的序列即可。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int n,a[N];

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int las=3,ans=0;
	for(int i=2;i<=n;i++){
		if(a[i]==a[i-1]);
		else if(a[i]>a[i-1]){
			if(las==1) las=3,ans++;
			else las=2;
		}
		else{
			if(las==2) las=3,ans++;
			else las=1;
		}
	}
	ans++;
	printf("%d\n",ans);
}

B - Hamiltonish Path

考虑 d f s dfs dfs树不存在横叉边,那么我们只需要找 d f s dfs dfs中一条极长的链即可,这样路径上只有树边和返祖边,满足条件。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
struct edge{
	int y,nex;
}s[N<<1];
int first[N],len=0,n,m;
bool vis[N];
vector<int> V;

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;
}

void dfs_1(int x){
	vis[x]=true;
	for(int i=first[x];i!=0;i=s[i].nex) if(!vis[s[i].y]){
		dfs_1(s[i].y);break;
	}
	V.push_back(x);
}

void dfs_2(int x){
	vis[x]=true;
	V.push_back(x);
	for(int i=first[x];i!=0;i=s[i].nex) if(!vis[s[i].y]){
		dfs_2(s[i].y);break;
	}
}

int main(){
	scanf("%d %d",&n,&m);
	int x,y;
	for(int i=1;i<=m;i++){
		scanf("%d %d",&x,&y);
		ins(x,y),ins(y,x);
	}
	dfs_1(1);V.pop_back();dfs_2(1);
	printf("%d\n",V.size());
	for(int i=0;i<V.size();i++) printf("%d ",V[i]);
}

C - Ants on a Circle

好牛逼
首先序列上的问题就是无视每一次翻转方向,计算出每一只蚂蚁最后的位置之后,拍个序就知道每一只蚂蚁的位置。
环上的问题也是无视每一次的翻转方向,但是每一次有蚂蚁经过 0 0 0这条分界线的时候,编号为 1 1 1的位置就会偏移一位,计算偏移了几位,直接按顺序输出所有的解就可以了。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int a[N],n,L,T;

int main(){
	scanf("%d %d %d",&n,&L,&T);
	int x,w,ans=1;
	for(int i=1;i<=n;i++) {
		scanf("%d %d",&x,&w);
		if(w==2) w=-1;
		a[i]=x+w*T;
		if(a[i]<0) ans=(ans+n-(L-1-a[i])/L)%n;
		else if(a[i]>=L) ans=(ans+a[i]/L)%n;
		a[i]=(a[i]%L+L)%L;
	}
	if(ans==0) ans=n;
	sort(a+1,a+1+n);
	for(int i=ans;i<=n;i++) printf("%d\n",a[i]);
	for(int i=1;i<ans;i++) printf("%d\n",a[i]);
}

D - Piling Up

好像和其他人的做法不太一样。
首先第一个拿出来的直接丢掉, n − = 1 n-=1 n=1即可,方案数最后乘上 2 2 2,最后一步肯定两种颜色都能取,所以也是给方案数最后乘上 2 2 2。先给 m − = 1 m-=1 m=1
那么现在就要考虑中间 m m m次的“放两个,取两个”的方案数。
相当于就有三种方案,放 B B B R R R,放 R R R B B B,不放不取。
考虑一条从 ( 0 , 0 ) (0,0) (0,0)开始的折线,当前点为 ( x , y ) (x,y) (x,y)
若放 R R R B B B,那么移动到 ( x + 1 , y + 1 ) (x+1,y+1) (x+1,y+1)
若放 B B B R R R,那么移动到 ( x + 1 , y − 1 ) (x+1,y-1) (x+1,y1)
若不放不取,那么移动到 ( x + 1 , y ) (x+1,y) (x+1,y)
结论: n n n的限制相当于使得 t = max ⁡ { y i } − min ⁡ { y i } ≤ n t=\max\{y_i\}-\min\{y_i\}\leq n t=max{yi}min{yi}n
证明:考虑这样构造方案。
不放不取直接跳过
如果超过了最大值,那么从剩下的球中拿出来一个未染色的染成蓝色,最小值同理,而且容易证明没有其他方案。
如果没有超过,那么观察这条折线向左看去的第一条折线,这条折线一定与其是反向的。
这样,我们就可以拿出那时候放回去的球,再放回去一个相反颜色的球。
那么现在相当于要求一条长度为 m m m的折线,满足 t ≤ n t\leq n tn
稍微思考一下这就变成了一个简单的 D p Dp Dp,我们只需要将最小值移回 0 0 0就可以了。
D p Dp Dp时记录一下是否到过 0 0 0即可,注意不放不取的方案数为 2 2 2
但博主考虑在第一次到达 0 0 0时计算答案,需要两个 D p Dp Dp的答案拼起来。

#include<bits/stdc++.h>
using namespace std;

const int N=3010,mod=1000000007;
int f[N][N],g[N][N],n,m;

void ad(int&x,int y){x=(x+y>=mod)?(x+y-mod):(x+y);}

int main(){
	scanf("%d %d",&n,&m);n--;m--;
	for(int i=0;i<=n;i++) f[0][i]=g[0][i]=1;
	for(int i=0;i<m;i++){
		for(int j=0;j<=n;j++) if(g[i][j]){
			if(j!=0) ad(g[i+1][j-1],g[i][j]);
			if(j!=n) ad(g[i+1][j+1],g[i][j]);
			ad(g[i+1][j],g[i][j]);
			ad(g[i+1][j],g[i][j]);
		}
	}
	for(int i=0;i<m;i++){
		for(int j=1;j<=n;j++) if(f[i][j]){
			ad(f[i+1][j-1],f[i][j]);
			if(j!=n) ad(f[i+1][j+1],f[i][j]);
			ad(f[i+1][j],f[i][j]);
			ad(f[i+1][j],f[i][j]);
		}
	}
	int ans=0;
	for(int i=0;i<=m;i++)
		ad(ans,1ll*f[i][0]*g[m-i][0]%mod);
	ad(ans,ans);ad(ans,ans);
	printf("%d\n",ans);
}

E - Placing Squares

将一个长度为 l l l的区间的贡献 l 2 l^2 l2这样考虑:
在这个区间中有序的选出两个位置。
可以 O ( n ) D p O(n)Dp O(n)Dp,使用矩阵快速幂优化。
有关键点的位置相当于不能新开一个区间。
时间复杂度就变成 O ( 3 3 log ⁡ n + 3 2 m log ⁡ n ) O(3^3\log n+3^2m\log n) O(33logn+32mlogn)

using namespace std;

const int mod=1000000007;
struct node{
	int d[3][3];
}tot,X[30],Y;
int n,m;
node operator*(const node&a,const node&b){
	node q;
	for(int i=0;i<3;i++) for(int j=0;j<3;j++) q.d[i][j]=0;
	for(int i=0;i<3;i++)
		for(int k=0;k<3;k++)
			for(int j=0;j<3;j++)
				q.d[i][j]=(q.d[i][j]+1ll*a.d[i][k]*b.d[k][j])%mod;
	return q;
}

void go(int x){
	for(int i=0;i<30;i++) if(x&(1<<i)) tot=tot*X[i];
}

int main(){
	scanf("%d %d",&n,&m);
	X[0].d[0][0]=X[0].d[2][0]=X[0].d[0][1]=X[0].d[1][1]
	=X[0].d[0][2]=X[0].d[2][1]=1;X[0].d[1][2]=X[0].d[2][2]=2;
	Y=X[0];Y.d[2][0]=0;Y.d[2][2]=1;Y.d[2][1]=0;
	tot.d[0][0]=tot.d[1][1]=tot.d[2][2]=1;
	for(int i=1;i<30;i++) X[i]=X[i-1]*X[i-1];
	int las=0,x=0;
	for(int i=1;i<=m;i++){
		scanf("%d",&x);
		go(x-las);las=x+1;
		tot=tot*Y;
	}
	go(n-las);
	printf("%d\n",tot.d[0][2]);
}

F - Two Faced Cards

牛逼题
如何入手?
发现最后要让两两匹配。
若确定了方向,那么相当于 Z i ≤ Y i Z_i\leq Y_i ZiYi,排个序就能解决。
但我们不排序,考虑换一种思路:
首先按 Y i Y_i Yi Z , Y Z,Y Z,Y离散化,如何判定?
新建一个数组 T T T,给每一个 T [ A i , n ] T_{[A_i,n]} T[Ai,n] 1 1 1,给 T [ B i , n ] T_{[B_i,n]} T[Bi,n] 1 1 1
T i T_i Ti全非负即可。方案也很好构造,每次找到最大的 Z i , Y i Z_i,Y_i Zi,Yi取走即可。
那么这道题现在就变成了,先给所有的 T [ A i , n ] + 1 T_{[A_i,n]}+1 T[Ai,n]+1,再给所有的 T [ C i , n ] − 1 T_{[C_i,n]}-1 T[Ci,n]1
每次可以花费 1 1 1的代价给 [ B i , A i ) + 1 [B_i,A_i)+1 [Bi,Ai)+1,定义 a n s [ x ] ans[x] ans[x]为使得 T i ≥ 0 ∣ i ∈ [ 1 , x ) , T i ≥ − 1 ∣ i ∈ [ x , n ] T_i\geq 0|i\in[1,x),T_i\geq -1|i\in[x,n] Ti0i[1,x),Ti1i[x,n]的最小代价,第 i i i组答案即为 min ⁡ ( a n s [ D i ] , a n s [ E i ] + 1 ) \min(ans[D_i],ans[E_i]+1) min(ans[Di],ans[Ei]+1)
显然我们要将所有的 a n s [ i ] ans[i] ans[i]处理出来。
考虑先求出 a n s [ 1 ] ans[1] ans[1],可以采取正着或者倒着贪心,以倒着贪心为例。
每次遍历到一个点若发现 T i < − 1 T_i<-1 Ti<1,那么就找到左端点最小且包含该点的区间,使其 + 1 +1 +1,如果不存在,那么不存在方案,证明显然。
正着贪心其实是不行的,因为我们的这个 T i ≥ 0 ∣ i ∈ [ 1 , x ) , T i ≥ − 1 ∣ i ∈ [ x , n ] T_i\geq 0|i\in[1,x),T_i\geq -1|i\in[x,n] Ti0i[1,x),Ti1i[x,n]限制。
对于 a n s [ x ] ans[x] ans[x]来说要使得前缀操作次数尽可能小,倒着贪心的时候可以做到这一点,正着贪心不行。
处理其他的 a n s [ x ] ans[x] ans[x]正着贪心过去即可,每次找到一个未被使用包含该点右端点最大的区间,不存在则无解。

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
using namespace std;

const int N=100010;
int a[N],b[N],c[N],d[N],n,q,ans[N];
vector<int> L[N],R[N];
int sum[N],m;
priority_queue<pair<int,int> > qs;
pair<int,int> X;

void add(int x,int t){
	while(x<=m){
		sum[x]+=t;
		x+=lowbit(x);
	}
}

int gs(int x){
	int tot=0;
	while(x){
		tot+=sum[x];
		x-=lowbit(x);
	}
	return tot;
}

int main(){
	scanf("%d",&n);n++;
	for(int i=1;i<n;i++) scanf("%d %d",&a[i],&b[i]);
	for(int i=1;i<=n;i++) scanf("%d",&c[i]),d[i]=c[i];
	sort(d+1,d+1+n);m=unique(d+1,d+1+n)-d-1;d[++m]=1e9+1;
	for(int i=1;i<n;i++){
		a[i]=lower_bound(d+1,d+1+m,a[i])-d;
		b[i]=lower_bound(d+1,d+1+m,b[i])-d;
		c[i]=lower_bound(d+1,d+1+m,c[i])-d;
		add(a[i],1);add(c[i],-1);
		if(b[i]<a[i]) R[a[i]-1].push_back(b[i]);
	}
	c[n]=lower_bound(d+1,d+1+m,c[n])-d;
	add(c[n],-1);
	for(int i=m;i>=1;i--) {
		for(int j=0;j<R[i].size();j++)
			qs.push(make_pair(-R[i][j],i));
		int tmp=gs(i);
		while(!qs.empty() && tmp<-1){
			X=qs.top();qs.pop();
			add(-X.first,1);add(X.second+1,-1);
			tmp++;ans[0]++;
		}
		if(tmp<-1) {ans[0]=1e9;break;}
	}
	while(!qs.empty()) X=qs.top(),L[-X.first].push_back(X.second),qs.pop();
	for(int i=1;i<=m;i++){
		for(int j=0;j<L[i].size();j++) 
			qs.push(make_pair(L[i][j],i));
		int tmp=gs(i);ans[i]=ans[i-1];
		while(!qs.empty() && tmp<0){
			X=qs.top();qs.pop();
			add(X.second,1);add(X.first+1,-1);
			tmp++;ans[i]++;
		}
		if(tmp<0) ans[i]=1e9;
	}
	scanf("%d",&q);
	int x,y;
	while(q--){
		scanf("%d %d",&x,&y);
		x=lower_bound(d+1,d+1+m,x)-d;
		y=lower_bound(d+1,d+1+m,y)-d;
		int mmin=min(ans[x-1],ans[y-1]+1);
		printf("%d\n",mmin>=1e9?-1:n-mmin);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值