AtCoder Beginner Contest 190
不难。
A - Very Very Primitive Game
题意:两个人吃糖,每次吃一颗,(C=0)表示第一个人先吃,(C=1)表示第二个人先吃,先无糖可吃的人就输了,问谁赢。
简单分类讨论,如果差值大于(1)一定是糖多的赢,对(abs(A-B)=1)的特殊处理一下即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
using namespace std;
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<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int main()
{
int A=read(),B=read(),C=read();
if(A-B>=2)return puts("Takahashi"),0;
if(B-A>=2)return puts("Aoki"),0;
if(C==1&&A>=B)return puts("Takahashi"),0;
if(C==1&&A<B)return puts("Aoki"),0;
if(C==0&&B>=A)return puts("Aoki"),0;
return puts("Takahashi"),0;
}
B - Magic 3
题意:一个人用(n)次技能打怪物,第(i)个技能触发时间为(x_i),威力为(y_i),怪物能免疫触发时间大于等于(S)技能的和威力小于等于(D)的技能,问是否有技能能够打到怪物身上。
按顺序读入,判断是否有满足条件的技能即可(ABC第二题比第一题简单是传统艺能吗)
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
using namespace std;
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<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int n,S,D;
int main()
{
n=read();S=read();D=read();
while(n--)
{
int x=read(),y=read();
if(x<S&&y>D)return puts("Yes"),0;
}
return puts("No"),0;
}
C - Bowls and Dishes
题意:(n)个盘子(m)个条件,第(i)个条件被满足当且仅当第(a_i)个盘子和第(b_i)个盘子都不为空。有(k)个人过来,第(i)个人可以往第(c_i)个盘子里放一个球,或往第(d_i)个盘子里边放一个球,问最多有多少条件被满足。
感觉应该是有贪心或者其他的做法的,但是这题(k)很小,因此直接二进制枚举第(i)个人放到第(c_i)个中还是第(d_i)个中,之后(O(n))统计一下答案即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
using namespace std;
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<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,k,a[N],b[N],c[N],d[N],vis[101];
int main()
{
n=read();m=read();
for(int i=1;i<=m;i++)
{
a[i]=read();
b[i]=read();
}
k=read();
for(int i=1;i<=k;i++)
{
c[i]=read();
d[i]=read();
}
int maxn=0;
for(int i=0;i<(1<<k);i++)
{
memset(vis,0,sizeof(vis));
for(int j=1;j<=k;j++)
{
if(i&(1<<(j-1)))vis[c[j]]=1;
else vis[d[j]]=1;
}
int ans=0;
for(int j=1;j<=m;j++)ans+=(vis[a[j]]&vis[b[j]]);
maxn=max(maxn,ans);
}
printf("%d
",maxn);
return 0;
}
D - Staircase Sequences
题意:问和为(N)公差为(1)的等差数列的个数。
看到等差数列求和就直接列求和公式,设(x)为首项,(k)为项数,则:
把(2)移过去
那很明显(k)就是(2N)的一个因子,而一个满足条件的(k)需要让(x)为正整数,(O(sqrt {N}))枚举(2N)的因子即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
#define int long long
using namespace std;
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<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int n,ans;
int check(int k)
{
if(((n/k)+1-k)%2!=0)return 0;
return 1;
}
signed main()
{
n=read()*2ll;
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
ans+=check(i);
if(i*i!=n)ans+=check(n/i);
}
}
printf("%lld
",ans);
return 0;
}
E - Magical Ornament
题意:有(N)种珠子,(M)种排列方式,第(i)种珠子和第(j)种珠子可以挨在一起摆放当且仅当存在(a_x=i,b_x=j)或(a_x=j,b_x=i),现在有(k)种珠子要穿在一起,求串的最小长度。
正解好像是(O(2^KK^2+K(N+M)))的,我写的是(O(2^KK^3+K(N+M)))的(dp+bfs)。
我这个做法还是比较好想的,看到这熟悉的形式,就想到了将(a_x)和(b_x)间连一条无向边,那么取完(i)再取(j)的最优方式就是在这张图上把(i)和(j)最短路上的珠子都取了。
那首先就可以预处理出来(dis(i,j))表示取完(i)再取(j)的最少取得数量,由于我们只需要关键点(就是那些要求的珠子)的(dis),因此对于每个关键点(bfs)一发即可求出(dis)。
由于(k)非常的小,可以用状压(dp),设(f(i,S))表示选了(i)个珠子集合状态为(S)的最小价值,考虑转移时枚举最后一个珠子和倒数第二个珠子。
但是这样做是有问题的,比如说我们现在有(4)个珠子,枚举最后一个珠子是(3),而倒数第二个珠子是(2),那么转移就是从(f(3,(1011)_2)+dis(2,3))转移过来的,但是问题在于:(f(3,(1011)_2))有可能最后一个取得不是(2),那么我们就歇逼了。
那考虑直接再加一维表示最后一个取的珠子,即(f(i,S,j))表示取了(i)个珠子,关键点集合状态为(S),最后一个取的珠子是(j),转移直接枚举最后两个取的即可。
答案直接枚举最后一个取的珠子即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define N 100005
#define MAXN 18
#define pb push_back
using namespace std;
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<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,k,f[18][1<<18][18],C[N],dis[18][N],vis[N];
vector<int>G[N];
void bfs(int S)
{
queue<int>q;
memset(vis,0,sizeof(vis));
vis[C[S]]=1;
dis[S][C[S]]=0;
q.push(C[S]);
while(!q.empty())
{
int x=q.front();q.pop();int siz=G[x].size();
for(int i=0;i<siz;i++)
{
if(!vis[G[x][i]])
{
dis[S][G[x][i]]=dis[S][x]+1;
vis[G[x][i]]=1;
q.push(G[x][i]);
}
}
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
G[x].pb(y);G[y].pb(x);
}
k=read();
for(int i=1;i<=k;i++)C[i]=read();
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=k;i++)bfs(i);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=k;i++)f[1][1<<(i-1)][i]=1;
for(int i=2;i<=k;i++)
{
for(int S=0;S<(1<<k);S++)
{
for(int j=1;j<=k;j++)
{
if(!(S&(1<<(j-1))))continue;
for(int l=1;l<=k;l++)
{
if(!(S&(1<<(l-1))))continue;
f[i][S][j]=min(f[i][S][j],f[i-1][S-(1<<(j-1))][l]+dis[j][C[l]]);
}
}
}
}
int ans=0x3f3f3f3f;
for(int i=1;i<=k;i++)
{
ans=min(ans,f[k][(1<<k)-1][i]);
}
printf("%d
",ans==0x3f3f3f3f?-1:ans);
return 0;
}
F - Shift and Inversions
题意:一个排列(a),定义一个(a)的置换(b)为(b_i = a_{i+k mod N}),求对于每一个(kin[0,N-1]),所对应的(b)的逆序对数量。
说实话没E题难。
首先这个(b)就是每一次把(a)数组的第一个数取出来放到最后一个。
为了方便,我们先把(a)中所有数都加(1),让它变成一个排列,方便操作。
那么考虑当前第一个数为(x),把他扔到最后一个会有怎样的影响呢?
首先因为第一个数为(x),那么(1 o x-1)中所有数一定都在第一个的后边(废话),因此扔到后边逆序对数会减少(x-1),但同时因为比(x)大的数也都在后边,那会多出来(n-x)对逆序对。
因此只需要处理(k=0)的情况,之后依次看第一个数即可。
静态序列的逆序对数量是个经典题,树状数组解决即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#define lowbit(x) x&-x
#define N 300005
#define int long long
using namespace std;
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<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int n,a[N],ans;
struct BIT
{
int c[N];
void modify(int x,int k){for(;x<=n;x+=lowbit(x))c[x]+=k;}
int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
}T;
signed main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read()+1;
for(int i=1;i<=n;i++)
{
ans+=i-1-T.query(a[i]);
T.modify(a[i],1);
}
for(int i=1;i<=n;i++)
{
printf("%lld
",ans);
ans-=(a[i]-1);ans+=n-a[i];
}
}
感觉不是非常难,下次vp一下ARC。