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

A - World Cup

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

#include
#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
#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 j j,当 a j = j a_j=j aj=j 时满足条件的 i i i 需有 a i = i a_i=i ai=i,否则只能 i = a j i=a_j i=aj,需有 a a j = j a_{a_j}=j aaj=j

#include
#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
#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 0 0,蓝色点权值为 1 1 1,那么边 i i i 的边权 W i = x u W_i=x_{u} Wi=xu xor \text{xor} xor x v x_{v} xv

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

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

#include
#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 k k 的后缀序列移动到最前面,用和上一种情况相同的策略解决问题。显然这个后缀序列的开头一定是 min ⁡ i = n − k + 1 n { p i } \min\limits_{i=n-k+1}^n{\{p_i\}} i=nk+1minn{pi}

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

#include
#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

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

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

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

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

#include
#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 某位歌姬的故事 ,直接参照原题题解即可。(说白了就是我又菜又懒)

你可能感兴趣的:(题解,c++,算法)