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