AtCoder Grand Contest 019
Posted cjjsb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Grand Contest 019相关的知识,希望对你有一定的参考价值。
Preface
最后四天,最后四场!
A - Ice Tea Store
有手就行,考虑偶数部分和多出的一个(0/1)分别用什么买最优即可
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int a,b,c,d,n;
int main()
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&n);
return printf("%lld",1LL*(n/2)*min(8*a,min(4*b,min(2*c,d)))+(n&1)*min(4*a,min(2*b,c))),0;
}
B - Reverse and Compare
刚开始想的贼复杂,后来被陈指导提示了才会做
考虑可操作的区间数量是(frac{n(n+1)}{2}+1),我们考虑哪些区间是不合法的
直观地,若([l,r])满足(s_l=s_r),那么他得到的结果和([l+1,r-1])显然是相同的
因此我们对于每种字符求出它出现的个数统计一下对数即可
#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int n,c[26]; char s[N]; long long ans;
int main()
{
RI i; for (scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i) ++c[s[i]-‘a‘];
for (ans=1LL*n*(n+1)/2LL+1,i=0;i<26;++i) ans-=1LL*c[i]*(c[i]+1)/2LL;
return printf("%lld",ans),0;
}
C - Fountain Walk
去年大概这个时候模拟赛考过,一年后再写感觉细节也挺少的
首先一个直观的想法,在直接走过去的路上走尽量多的(frac{1}{4})圆可以使路径长度减少(20-5pi)
假设(x_1<x_2,y_1<y_2),我们把所有点都按(x_i)排序之后找出在两点之间的点,按(y_i)做一个LIS就是能经过的最多的(frac{1}{4})个数了,(y_1ge y_2)同理
但是我们发现有些情况一定会经过半圆,画画图分析下此时若LIS长度(<min(|x_1-x_2|,|y_1-y_2|)+1)那必然要经过一个半圆,多走(10pi -20)的路
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
const double pi=acos(-1);
struct point
{
int x,y;
friend inline bool operator < (const point& A,const point& B)
{
return A.x<B.x;
}
inline void input(void) { scanf("%d%d",&x,&y); }
}p[N],s,t; int rst[N],n,ans,tp;
#define lowbit(x) (x&-x)
class Tree_Array1
{
private:
int bit[N];
public:
inline int get(RI x,int ret=0)
{
for (;x;x-=lowbit(x)) ret=max(ret,bit[x]); return ret;
}
inline void add(RI x,CI y)
{
for (;x<=n;x+=lowbit(x)) bit[x]=max(bit[x],y);
}
}BIT1;
class Tree_Array2
{
private:
int bit[N];
public:
inline int get(RI x,int ret=0)
{
for (;x<=n;x+=lowbit(x)) ret=max(ret,bit[x]); return ret;
}
inline void add(RI x,CI y)
{
for (;x;x-=lowbit(x)) bit[x]=max(bit[x],y);
}
}BIT2;
#undef lowbit
int main()
{
RI i; s.input(); t.input(); if (s.x>t.x) swap(s,t);
for (scanf("%d",&n),i=1;i<=n;++i) p[i].input(),rst[i]=p[i].y;
for (sort(rst+1,rst+n+1),i=1;i<=n;++i) p[i].y=lower_bound(rst+1,rst+n+1,p[i].y)-rst;
for (sort(p+1,p+n+1),i=1;i<=n;++i)
if (p[i].x>=s.x&&p[i].x<=t.x&&rst[p[i].y]>=min(s.y,t.y)&&rst[p[i].y]<=max(s.y,t.y))
{
if (s.y<=t.y) ans=max(ans,tp=BIT1.get(p[i].y)+1),BIT1.add(p[i].y,tp);
else ans=max(ans,tp=BIT2.get(p[i].y)+1),BIT2.add(p[i].y,tp);
}
double ret=100LL*(abs(s.x-t.x)+abs(s.y-t.y));
if (ans<min(abs(s.x-t.x),abs(s.y-t.y))+1) printf("%.15lf",ret-ans*(20-5*pi));
else printf("%.15lf",ret-(ans-1)*(20-5*pi)+10*pi-20); return 0;
}
D - Shift and Flip
都是陈指导指导的好,考虑一个(O(n^3))暴力,枚举向左位移次数和向右位移次数,此时每个位置要变成什么已经确定了,预处理出每个位置左/右边最近的(1)再枚举每个位置判断下即可
然后我们发现每个位置最后变成什么之和左位移次数和向右位移次数的差值有关,因此我们考虑先枚举差值
接下来需要一点转化,因为直接枚举左位移次数不方便我们检验,我们枚举左位移次数的极值,即向左最远能到哪里
设每个位置(i)左/右边最近的(1)与该位置的距离为(l_i,r_i),此时枚举的左位移次数的极值为(ml),那么对于每个要修改的点只需要讨论下:
- (l_ile ml),这个点可以直接被改掉,不做处理
- (l_i>ml),这个点现在只能在右边被改掉,那么相应的可以统计出此时右位移次数的极值(mr=max r_i)
很显然把((l_i,r_i))按(l_i)排序再从大到小扫一边即可,但由于它们的值域也是(O(n))的因此直接开个桶即可
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=2005;
int n,ans=1e9,l[N],r[N],v[N]; char a[N],b[N];
int main()
{
RI i,j; scanf("%s%s",a,b); n=strlen(a);
for (i=0;i<n;++i)
{
for (j=i;b[j]!=‘1‘&&l[i]<n;j=(j-1+n)%n,++l[i]);
for (j=i;b[j]!=‘1‘&&r[i]<n;j=(j+1)%n,++r[i]);
if (l[i]==n)
{
bool flag=1; for (j=0;j<n;++j) flag&=a[j]==‘0‘;
puts(flag?"0":"-1"); return 0;
}
}
for (i=0;i<n;++i)
{
for (j=0;j<n;++j) v[j]=0; int ct=0;
for (j=0;j<n;++j) if (a[j]!=b[(j+i)%n]) ++ct,v[l[j]]=max(v[l[j]],r[j]);
int mr=0; for (j=n-1;~j;--j)
{
if (mr<=i) ans=min(ans,ct+2*j+i); else ans=min(ans,ct+2*j+mr+mr-i); mr=max(mr,v[j]);
}
}
for (i=0;i<n;++i)
{
for (j=0;j<n;++j) v[j]=0; int ct=0;
for (j=0;j<n;++j) if (a[j]!=b[(j-i+n)%n]) ++ct,v[l[j]]=max(v[l[j]],r[j]);
int mr=0; for (j=n-1;~j;--j)
{
if (i>=j) ans=min(ans,ct+2*mr+i); else ans=min(ans,ct+2*mr+j+j-i); mr=max(mr,v[j]);
}
}
return printf("%d",ans),0;
}
E - Shuffle and Swap
真神的一道题,被教育了
首先转化一下题目的要求,把题目看做让(A)中多余的(1)换到(A)中缺少(1)的地方去,具体地,我们定义:
- 公共点:满足(A_i=1land B_i=1)。此时显然无论如何交换,该位置上的值都为(1)
- 起点:满足(A_i=1land B_i=0)。此时我们需要把该位置上的(1)转移走
- 终点:满足(A_i=0land B_i=1)。此时我们需要把该位置上转移来一个(1)
此时可以发现可以使得(A=B)的操作序列满足:
连接((b_i,a_i)),图的形态由若干由2,3类点作为端点,1类点作为中间点的有向链(即不能倒着操作一条链)
我们设(f_{i,j})表示使用了(i)个公共点,组成了(j)条链的原序列(即(a,b))的方案数,发现转移:
- 加入一个新的公共点,首先我们选取它所在的链的,并钦定它在末尾,在乘上标号,此时的贡献就是(f_{i-1,j} imes i imes j)
- 加入一个新的链,还是钦定它放在末尾,考虑此时起点和终点的标号,由于是在原序列上,不难发现此时的贡献就是(f_{i,j-1} imes j^2)
由于每次转移会导致原序列的长度加(1),因此我们同样钦定每次新增后放在末尾
因为此时一条链可能会对应许多种原序列,我们要用不同的转移顺序来区分它们
接下来考虑统计答案,我们枚举公共点有多少个不放在链上,设一共有(s)个公共点,(t)条链,则:
总复杂度为(frac{|A|^2}{4}),可以通过此题,多项式的做法就懒得写了(其实是不会)
#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=10005,mod=998244353;
int n,f[N][N],fact[N],inv[N],s,t,ans; char a[N],b[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
RI i,j; for (scanf("%s%s",a+1,b+1),n=strlen(a+1),i=1;i<=n;++i)
if (a[i]==‘1‘&&b[i]==‘1‘) ++s; else if (a[i]==‘1‘&&b[i]==‘0‘) ++t;
for (init(n),f[0][0]=1,i=0;i<=s;++i) for (j=0;j<=t;++j)
(f[i][j]+=1LL*f[i-1][j]*i%mod*j%mod)%=mod,(f[i][j]+=1LL*f[i][j-1]*j%mod*j%mod)%=mod;
for (i=0;i<=s;++i) (ans+=1LL*C(s,i)*fact[i]%mod*fact[i]%mod*C(s+t,i)%mod*f[s-i][t]%mod)%=mod;
return printf("%d",ans),0;
}
以上是关于AtCoder Grand Contest 019的主要内容,如果未能解决你的问题,请参考以下文章
markdown AtCoder Grand Contest 016