AtCoder Beginner Contest 262(ABC262)A-Ex 题解

A - World Cup

我懒得分类讨论,直接枚举。

#include<bits/stdc++.h>
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;



inline int read()
{
	int x=0,f=1;static char ch;
	while(ch=getchar(),ch<48)if(ch==45)f=0;
	do x=(x<<1)+(x<<3)+(ch^48);
	while(ch=getchar(),ch>=48);
	return f?x:-x;
}



int main()
{
	int n=read();
	while(n%4!=2)n++;
	printf("%d",n);
	
	return 0;
}

B - Triangle (Easier)

暴力存边,枚举即可。

#include<bits/stdc++.h>
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;

const int M=105;

inline int read()
{
	int x=0,f=1;static char ch;
	while(ch=getchar(),ch<48)if(ch==45)f=0;
	do x=(x<<1)+(x<<3)+(ch^48);
	while(ch=getchar(),ch>=48);
	return f?x:-x;
}

int n,m,ans;
bool s[M][M];

int main()
{
	n=read(),m=read();
	for(int i=1,u,v;i<=m;i++)
	{
		u=read(),v=read();
		s[u][v]=s[v][u]=true;
	}
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			for(int k=j+1;k<=n;k++)
				if(s[i][j]&&s[j][k]&&s[i][k])
					ans++;
	printf("%d",ans);
	
	return 0;
}

C - Min Max Pair

枚举 \(j\),当 \(a_j=j\) 时满足条件的 \(i\) 需有 \(a_i=i\),否则只能 \(i=a_j\),需有 \(a_{a_j}=j\)

#include<bits/stdc++.h>
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;
 
const int M=5e5+5;
 
inline int read()
{
	int x=0,f=1;static char ch;
	while(ch=getchar(),ch<48)if(ch==45)f=0;
	do x=(x<<1)+(x<<3)+(ch^48);
	while(ch=getchar(),ch>=48);
	return f?x:-x;
}
 
int n,a[M],sum[M],sn;
long long ans;
 
int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	
	for(int i=1;i<=n;i++)
	{
		if(a[i]==i)ans+=sn;
		else if(a[i]<i&&a[a[i]]==i)ans++;
		if(a[i]==i)sn++;
	}
	printf("%lld",ans);
	
	return 0;
}

D - I Hate Non-integer Number

枚举选取的个数,直接跑背包即可。

#include<bits/stdc++.h>
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;

typedef long long ll;
const ll P=998244353;
const int M=105;

inline int read()
{
	int x=0,f=1;static char ch;
	while(ch=getchar(),ch<48)if(ch==45)f=0;
	do x=(x<<1)+(x<<3)+(ch^48);
	while(ch=getchar(),ch>=48);
	return f?x:-x;
}

int n,a[M];
ll dp[M][M],ans;

void ADD(ll &x,ll y){(x+=y)>=P?x-=P:x;}

int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	ans+=n;
	for(int num=2;num<=n;num++)
	{
		dp[0][0]=1ll;
		for(int i=1;i<=n;i++)
			for(int j=num-1;j>=0;j--)
				for(int k=0;k<num;k++)
					ADD(dp[j+1][(k+a[i])%num],dp[j][k]);
		ADD(ans,dp[num][0]);
		for(int j=0;j<=num;j++)
			for(int k=0;k<=num;k++)
				dp[j][k]=0;
	}
	printf("%lld",ans);
	
	return 0;
}

E - Red and Blue Graph

设红色点权值为 \(0\),蓝色点权值为 \(1\),那么边 \(i\) 的边权 \(W_i=x_{u}\) \(\text{xor}\) \(x_{v}\)

题目要求 \(W_i=1\) 的边数为偶数,那么所有边权值的异或和就为 \(0\)。如果设 \(D_i\) 表示点 \(i\) 的度数,那么 \(x_i\) 就会被异或上 \(D_i\) 次。

\(D_i\) 为偶数时或 \(x_i=0\) 时,贡献为 \(0\),否则贡献为 \(1\)。那么题目就转化为:在奇点中选取偶数个点染成红色且满足数量不超过 \(K\) 的方案数。这就等于 \(\sum\limits_{i=0}^{\min(\lfloor odd/2\rfloor,\lfloor k/2\rfloor)}{\binom{odd}{i}\times \binom{n-odd}{k-i}}\)

#include<bits/stdc++.h>
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;

typedef long long ll;
const ll P=998244353;
const int M=2e5+5;

inline int read()
{
	int x=0,f=1;static char ch;
	while(ch=getchar(),ch<48)if(ch==45)f=0;
	do x=(x<<1)+(x<<3)+(ch^48);
	while(ch=getchar(),ch>=48);
	return f?x:-x;
}

int n,m,k,deg[M],odd;
ll Fac[M],Inv[M],ans;
ll ksm(ll a,ll b)
{
	ll res=1ll;
	while(b)
	{
		if(b&1ll)res=res*a%P;
		a=a*a%P,b>>=1ll;
	}
	return res;
}
void ADD(ll &x,ll y){(x+=y)>=P?x-=P:x;}
ll C(ll n,ll m)
{
	if(n<0||m<0||n<m)return 0;
	return Fac[n]*Inv[m]%P*Inv[n-m]%P;
}

int main()
{
	n=read(),m=read(),k=read();
	for(int i=1,u,v;i<=m;i++)
	{
		u=read(),v=read();
		deg[u]++,deg[v]++;
	}
	
	Fac[0]=1ll;for(int i=1;i<=n;i++)Fac[i]=Fac[i-1]*i%P;
	Inv[n]=ksm(Fac[n],P-2);for(int i=n-1;i>=0;i--)Inv[i]=Inv[i+1]*(i+1)%P;
	
	for(int i=1;i<=n;i++)if(deg[i]&1)odd++;
	for(int i=0;i<=odd&&i<=k;i+=2)
		ADD(ans,C(odd,i)*C(n-odd,k-i)%P);
	printf("%lld",ans);
	
	return 0;
}

F - Erase and Rotate

发现所有操作可以变为先移动,后删除,其中删除掉被移动的数字不算入操作次数。

我们先考虑不使用移动操作。按数字从小到大贪心,如果能把当前序列中最小数字前的数字删光,那么就删光,然后对该数字后的序列如法炮制,否则选择次大数字重复上述步骤,依次推进。我们可以用单调队列维护这个序列。

然后考虑使用移动操作。注意到移动操作不会改变被移动的段的先后顺序,因此我们可以选择一段小于等于 \(k\) 的后缀序列移动到最前面,用和上一种情况相同的策略解决问题。显然这个后缀序列的开头一定是 \(\min\limits_{i=n-k+1}^n{\{p_i\}}\)

注意到,当操作不满 \(k\) 次时,我们可以将操作完的序列从后往前删,来使其字典序更小。

#include<bits/stdc++.h>
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;

const int M=2e5+5;

inline int read()
{
	int x=0,f=1;static char ch;
	while(ch=getchar(),ch<48)if(ch==45)f=0;
	do x=(x<<1)+(x<<3)+(ch^48);
	while(ch=getchar(),ch>=48);
	return f?x:-x;
}

int n,k,mov[M],fir;
bool mark[M];
deque<int> p;
vector<int> ans;

vector<int> solve()
{
	vector<int> res;int t=k;
	for(int x:p)
	{
		while(!res.empty()&&x<res.back())
		{
			if(mark[res.back()])res.pop_back();
			else if(t)t--,res.pop_back();
			else break;
		}
		res.push_back(x);
	}
	while(t)t--,res.pop_back();
	return res;
}

int main()
{
	n=read(),k=read();
	for(int i=1,x;i<=n;i++)
	{
		x=read(),mov[x]=n-i+1;
		p.push_back(x);
	}
	ans=solve();
	
	if(k)
	{
		for(int i=1;i<=n;i++)
			if(mov[i]<=k){fir=i;break;}
		while(p.front()!=fir)
		{
			mark[p.back()]=true;
			p.push_front(p.back());
			p.pop_back();k--;
		}
		ans=min(ans,solve());
	}
	
	for(int x:ans)printf("%d ",x);
	
	return 0;
}

G - LIS with Stack

最理想的状态,就是该删的都删了,而栈为空或自顶往下单调不降。

设计状态 \(dp(i,j,mi,mx)\) 表示处理完区间 \([i,j]\) 后得到的且元素大小在 \([mi,mx]\) 且单调不降的最长序列长度。接下来考虑转移:

  • 删除 \(a_i\)。那么有 \(dp(i,j,mi,mx)=dp(i+1,j,mi,mx)\)
  • 不删除 \(a_i\)。此时必定有 \(mi\le a_i\le mx\)
    • \(a_i\) 直接添加至 \(X\) 后。此时 \(dp(i,j,mi,mx)=1+dp(i+1,j,a_i,mx)\)
    • \(a_i\) 需先作为栈 \(S\) 的栈底,再被整体添加至 \(X\)。枚举 \(k\),表示整体添加操作在 \(a_k\) 压入栈后发生,那么 \(dp(i,j,mi,mx)=1+dp(i+1,k,mi,a_i)+dp(k+1,j,a_i,j)\)

那么答案即为 \(dp(1,n,1,50)\)

#include<bits/stdc++.h>
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;

const int M=55;

inline int read()
{
	int x=0,f=1;static char ch;
	while(ch=getchar(),ch<48)if(ch==45)f=0;
	do x=(x<<1)+(x<<3)+(ch^48);
	while(ch=getchar(),ch>=48);
	return f?x:-x;
}

int n,a[M],dp[M][M][M][M];

int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)
		for(int mi=1;mi<=a[i];mi++)
			for(int mx=a[i];mx<=50;mx++)
				dp[i][i][mi][mx]=1;
	
	for(int len=2;len<=n;len++)
		for(int i=1,j=i+len-1;j<=n;i++,j++)
			for(int mi=1;mi<=50;mi++)
				for(int mx=mi;mx<=50;mx++)
				{
					Max(dp[i][j][mi][mx],dp[i+1][j][mi][mx]);
					if(mi<=a[i]&&a[i]<=mx)
						for(int k=i;k<=j;k++)
							Max(dp[i][j][mi][mx],dp[i+1][k][mi][a[i]]+dp[k+1][j][a[i]][mx]+1);
				}
	printf("%d",dp[1][n][1][50]);
	
	return 0;
}

Ex - Max Limited Sequence

这题即 清华集训2017 某位歌姬的故事 ,直接参照原题题解即可。(说白了就是我又菜又懒)

posted @ 2022-08-01 20:05  cyl06  阅读(193)  评论(0编辑  收藏  举报