码迷,mamicode.com
首页 > 其他好文 > 详细

AtCoder Grand Contest 019

时间:2020-11-13 12:58:36      阅读:6      评论:0      收藏:0      [点我收藏+]

标签:接下来   friend   修改   add   表示   struct   wal   com   ret   

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-5\pi\)

假设\(x_1<x_2,y_1<y_2\),我们把所有点都按\(x_i\)排序之后找出在两点之间的点,按\(y_i\)做一个LIS就是能经过的最多的\(\frac{1}{4}\)个数了,\(y_1\ge y_2\)同理

但是我们发现有些情况一定会经过半圆,画画图分析下此时若LIS长度\(<\min(|x_1-x_2|,|y_1-y_2|)+1\)那必然要经过一个半圆,多走\(10\pi -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_i\le 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\)的地方去,具体地,我们定义:

  1. 公共点:满足\(A_i=1\land B_i=1\)。此时显然无论如何交换,该位置上的值都为\(1\)
  2. 起点:满足\(A_i=1\land B_i=0\)。此时我们需要把该位置上的\(1\)转移走
  3. 终点:满足\(A_i=0\land B_i=1\)。此时我们需要把该位置上转移来一个\(1\)

此时可以发现可以使得\(A=B\)的操作序列满足:

连接\((b_i,a_i)\),图的形态由若干由2,3类点作为端点,1类点作为中间点的有向链(即不能倒着操作一条链)

我们设\(f_{i,j}\)表示使用了\(i\)个公共点,组成了\(j\)条链的原序列(即\(a,b\))的方案数,发现转移:

  • 加入一个新的公共点,首先我们选取它所在的链的,并钦定它在末尾,在乘上标号,此时的贡献就是\(f_{i-1,j}\times i\times j\)
  • 加入一个新的链,还是钦定它放在末尾,考虑此时起点和终点的标号,由于是在原序列上,不难发现此时的贡献就是\(f_{i,j-1}\times j^2\)

由于每次转移会导致原序列的长度加\(1\),因此我们同样钦定每次新增后放在末尾

因为此时一条链可能会对应许多种原序列,我们要用不同的转移顺序来区分它们

接下来考虑统计答案,我们枚举公共点有多少个不放在链上,设一共有\(s\)个公共点,\(t\)条链,则:

\[ans=\sum_{i=0}^s C_s^i\times C_{s+t}^i\times (i!)^2\times f_{s-i,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

标签:接下来   friend   修改   add   表示   struct   wal   com   ret   

原文地址:https://www.cnblogs.com/cjjsb/p/13916527.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!