Preface

今年准备省选啥都不说了,省选题基本上都做过一遍了,开始尝试板刷AGC

这场做完就从AGC001开始吧,感觉以我的速度和来机房的频率一个礼拜做一场都谢天谢地了


A - ><

考虑我们找出所有的山谷的位置,把它们设成0

然后它有两种向两边扩展方式,直接取\(\min\)BFS一下就好了,显然状态数是\(2n\)的

#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=500005;
int n,q[N],val[N]; char s[N]; long long ans;
int main()
{
RI i,H=0,T=0; scanf("%s",s+1); n=strlen(s+1)+1;
for (i=1;i<=n;++i) if ((s[i-1]!='<')&&(s[i]!='>')) q[++T]=i;
while (H<T)
{
int now=q[++H];
if (now>1&&s[now-1]=='>'&&val[now-1]<val[now]+1) val[now-1]=val[now]+1,q[++T]=now-1;
if (now<n&&s[now]=='<'&&val[now+1]<val[now]+1) val[now+1]=val[now]+1,q[++T]=now+1;
}
for (i=1;i<=n;++i) ans+=val[i]; return printf("%lld",ans),0;
}

B - Two Contests

本以为我的做法挺妙的了然后陈指导给我看了他更妙的代码窝甘拜下风

首先我们找出两个区间\(p,q\),其中\(p\)是\(l\)最大的区间,\(q\)是\(r\)最小的区间

考虑两种情况,如果将\(p,q\)置于同一集合,那么显然此时它们之间的答案已经不可能再小了,直接从剩下的区间里挑一个最长的放在另一个集合里

否则考虑将\(p,q\)分开,此时我们的答案显然是这样(设\(p\in A,q\in B\))

\((\min_{i\in A} r_i-l_p)+(r_q-\max_{i\in B} l_i)\)

考虑将剩下的区间按\(l_i\)排序,然后处理出\(r_i\)的后缀最小值即可计算答案

注意特判\(n=2\)的情况

#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,INF=1e9;
struct interval
{
int l,r;
friend inline bool operator < (const interval& A,const interval& B)
{
return A.l<B.l;
}
}a[N],b[N]; int n,mxl=-1,mir=INF,mlen,tot,p,q,sufr[N],ans;
int main()
{
RI i; for (scanf("%d",&n),i=1;i<=n;++i)
{
scanf("%d%d",&a[i].l,&a[i].r);
if (a[i].l>mxl) mxl=a[i].l,p=i; if (a[i].r<mir) mir=a[i].r,q=i;
}
if (n==2) return printf("%d",a[1].r-a[1].l+1+a[2].r-a[2].l+1),0;
for (i=1;i<=n;++i) if (i!=p&&i!=q) b[++tot]=a[i],mlen=max(mlen,a[i].r-a[i].l+1);
for (sort(b+1,b+tot+1),i=tot-1,sufr[tot]=b[tot].r;i>0;--i) sufr[i]=min(sufr[i+1],b[i].r);
for (ans=mlen+max(mir-mxl+1,0),i=1;i<tot;++i)
ans=max(ans,max(mir-b[i].l+1,0)+max(sufr[i+1]-mxl+1,0));
ans=max(ans,a[q].r-a[q].l+1+max(sufr[1]-mxl+1,0));
ans=max(ans,max(mir-b[tot].l+1,0)+a[p].r-a[p].l+1);
return printf("%d",ans),0;
}

C - Neither AB nor BA

大脑短路卡在算答案的地方结果愣是花了两小时QAQ

首先我们搞一个转化,把所有偶数位上的字符都变换一下,即\(A\to B;B\to A;C\to C\),显然我们统计出转化后的字符串个数就是原来的答案(翻转是唯一对应的)

考虑这个时候我们的操作变成了不能删去连续的两个\(A\)或两个\(B\)

先不考虑\(C\),那么我们现在每次都是删除一个\(A\)和一个\(B\)

首先我们发现他成立的必要条件是\(A\)的个数等于\(B\)的个数,然后稍加分析发现它也是充分的

然后考虑有\(k\)个\(C\),由于每个\(C\)都可以让它带走一个\(A\)或\(B\)(我们总是选择带走数量多的那个),因此此时满足\(|N_A-N_B|\le N_C\)(\(N_A\)表示\(A\)的个数)

然后我就码出了一个\(n^2\)暴力,然后和陈指导找了一个多小时的规律在系数上(智力退化严重)

后来瞄了一眼题解发现只要容斥一下就好了,因为我们总是把\(C\)当做数量少的那种字符来用,因此如果多的那种字符数目超过了\(\frac{n}{2}\)就一定不合法

那么枚举多的数目直接统计即可

#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=1e7+5,mod=998244353;
int n,ret,fact[N],inv[N],pw[N];
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
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;
for (pw[0]=i=1;i<=n;++i) pw[i]=2LL*pw[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("%d",&n),init(n),i=(n>>1)+1;i<=n;++i)
inc(ret,1LL*C(n,i)*pw[n-i+1]%mod);
return printf("%d",(quick_pow(3,n)-ret+mod)%mod),0;
}

D - Balance Beam

妈呀不会,D感觉比E还难啊(窝好菜啊)

设\(sum=\sum_{i=1}^n \max(b_i-a_i,0)\)。首先我们假设已经知道了顺序,考虑一种贪心:

for (i=1;i<=n;++i)
if (sum>=b[i]) sum-=b[i],++ans; else { ans+=1.0*sum/b[i]; break; }

那么我们考虑枚举进入else语句里的\(i\)来算答案,我们记\(b_i-a_i\)的前缀\(\max\)位置为\(pmx\),因此贡献被分为\([1,i),(i.pmx],(pmx,n]\)三部分

然后我们发现如果我们把所有\(a_i<b_i\)的beam加到\((i,pmx]\)时,然后把多出的放到\(i\)前面,不难发现放到前面的代价是\(\max(a_i,b_i)\)

因此我们先按\(\max(a_i,b_i)\)排个序之后每次枚举\(i\)是尽量让前面的来的大,那么直接二分取得了整的前缀长度即可

#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
#define int long long
using namespace std;
const int N=100005;
struct frac
{
int x,y;
inline frac(CI X=0,CI Y=0)
{
x=X; y=Y;
}
friend inline bool operator < (const frac& A,const frac& B)
{
return 1.0*A.x*B.y<1.0*A.y*B.x;
}
friend inline bool operator > (const frac& A,const frac& B)
{
return 1.0*A.x*B.y>1.0*A.y*B.x;
}
}ans(0,1);
struct data
{
int a,b,val;
friend inline bool operator < (const data& A,const data& B)
{
return A.val<B.val;
}
}p[N]; int pfx[N],n,sum;
inline int gcd(CI x,CI y)
{
return y?gcd(y,x%y):x;
}
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (scanf("%lld",&n),i=1;i<=n;++i)
scanf("%lld%lld",&p[i].a,&p[i].b),
p[i].val=max(p[i].a,p[i].b),sum+=max(p[i].b-p[i].a,0LL);
for (sort(p+1,p+n+1),i=1;i<=n;++i) pfx[i]=pfx[i-1]+p[i].val;
#define calc(x) (pfx[x]-((x)>=i?p[i].val:0))
for (i=1;i<=n;++i)
{
int cur=sum; if (p[i].a>p[i].b) cur+=p[i].b-p[i].a;
int l=1,r=n,mid; while (l<=r)
if (calc(mid=l+r>>1)<=cur) l=mid+1; else r=mid-1;
//printf("%lld %lld\n",p[i].a,p[i].b);
frac ret(cur-calc(l-1),p[i].b); ret=min(ret,frac(1,1));
ret.x+=ret.y*(l-1-(l>i)); ans=max(ans,ret);
}
#undef calc
int g=gcd(ans.x,ans.y*=n); return printf("%lld %lld",ans.x/g,ans.y/g),0;
}

E - Prefix Suffix Addition

个人感觉比D清新多了,首先我们发现可以把两种操作分开来考虑

假设我们现在只考虑第一种操作,那么我们一定存在一种方案,使得所有非\(0\)的位置不相交(证明非常简单,如果有一种相交的方案那么一定一个把某一部分加到另一个上面去)

假设现在我在第\(i\)个位置上填了\(x_i\),那么产生贡献就是\(x_{i+1}<x_i\)时

那么我们类推第二种操作,设它填的数为\(y_i\),那么现在问题变成了:

满足\(i\in [1,n],x_i+y_i=a_i\)时

最小化\(\sum_{i=1}^n [x_{i+1}<x_i]+[y_{i+1}>y_i]\)

我们容易搞出一个DP,令\(f_{i,j}\)表示第\(i\)个位置\(x_i=j\)的最小代价,然后我们可以找出一些性质:

  • \(f_{i,j},j\in [0,a_i]\)单调不增,这个看一下算贡献的式子就能发现
  • \(f_{i,j},j\in[0,a_i]\)的极差\(\le 2\)

那么也就意味着我只要知道\(f_{i,0}\),然后记录下\(f_i\)的两个分界点即可

转移的时候也很简单,求\(f_i\)的分界点时只需要二分一下,然后\(f_{i-1}\)的肯定是尽量取小,那么取分界点讨论一下贡献即可

复杂度\(O(n\log n)\),据说有\(O(n)\)的做法(瑟瑟发抖)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
struct data
{
int p1,p2,val,a;
inline data(CI P1=0,CI P2=0,CI Val=0,CI A=0)
{
p1=P1; p2=P2; val=Val; a=A;
}
#define calc(lstx) ((lstx>nx)+(a-lstx<na-nx))
inline int get(CI na,CI nx)
{
int ret=val+calc(0); if (p1<=a) ret=min(ret,val-1+calc(p1));
if (p2<=a) ret=min(ret,val-2+calc(p2)); return ret;
}
#undef calc
}f[N]; int n;
int main()
{
RI i; for (scanf("%d",&n),f[0]=data(1,1),i=1;i<=n;++i)
{
scanf("%d",&f[i].a); f[i].val=f[i-1].get(f[i].a,0); int l,r,mid;
l=1; r=f[i].a; while (l<=r) if (f[i-1].get(f[i].a,mid=l+r>>1)==f[i].val)
l=mid+1; else r=mid-1; f[i].p1=l;
r=f[i].a; while (l<=r) if (f[i-1].get(f[i].a,mid=l+r>>1)==f[i].val-1)
l=mid+1; else r=mid-1; f[i].p2=l;
}
return printf("%d",f[n].get(0,0)),0;
}

F - Two Pieces

妙啊!可惜我当然是想不出来的(妈耶一种基本情况我想了快半小时才想通)

考虑把问题抽象化,由于两个碎片不可区分,因此我们可以设一个状态\((x,d)\)表示走的远的那个碎片在\(x\),另一个碎片距离它\(d\)的方案数,不难发现我们有下面三种转移:

  1. \(x,d\)加\(1\),表示较远的那个向前走了一步
  2. \(d\)减1,满足\(d\ge 2\),表示较远的那个向前走了一步,置于为什么要\(d\ge 2\)是为了在\(d=1\)时与第三种操作区分(因为它不是数操作方案构成的序列数)
  3. \(d=0\),表示把两个移到了一起

考虑我们先确定前面两种的放置方案数,然后插入第三种操作

不难发现第一种操作恰好被执行了\(B\)次,因此枚举第二种操作次数\(k\)

如果\(n=B+k\),那么我们现在相当于一个坐标系上的走路问题

显然这里第一步必须向右上走,然后我们把向下走转化成向右下走,限制变成不能碰到\(x\)轴,然后不合法的沿\(x\)轴翻转一下就可以统计方案数了(其实就是类Catalan数的统计方法)

否则剩下\(n-B-k\)个操作都是第三类,考虑它插入之后必须满足

  • 纵坐标能到达\(B-A\)
  • 不会使原来合法的第二种操作不合法

考虑它的影响相当于把一段后缀的\(d\)减去其中第一个\(d\)的值,因此这个位置必须是严格的后缀最小值

然后还要满足第一个限制,我们发现最后一次操作要满足此时的纵坐标为\(A-k\)

然后满足这些之后剩下的\(n-B-1-k\)个操作可以在所有\(d\le A-k\)的严格后缀最小值处插入,然后分析一下\(d\)的性质就会发现每个值都有且只有一个后缀最小值的位置

因此我们再用经典的隔板法计算一下方案数即可,综上便解决了这题

#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=1e7+5,mod=998244353;
int n,a,b,fact[N<<1],inv[N<<1],ans;
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
inline int sub(CI x,CI y)
{
int t=x-y; return t<0?t+mod:t;
}
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)
{
if (n<m) return 0; return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
RI i; scanf("%d%d%d",&n,&a,&b); if (!b) return puts("1"),0;
for (init(n<<1),i=0;i<=n-b&&i<=a;++i)
{
int ret=sub(C(b-1+i,b-1),C(b-1+i,b));
if (i==n-b) inc(ans,i==a?ret:0); else inc(ans,1LL*ret*C(n-b-i-1+a-i,a-i)%mod);
}
return printf("%d",ans),0;
}

Postscript

AGC真是发人深省呢……

AtCoder Grand Contest 040的更多相关文章

  1. 【AtCoder】AtCoder Grand Contest 040 解题报告

    点此进入比赛 \(A\):><(点此看题面) 大致题意: 给你一个长度为\(n-1\).由\(<\)和\(>\)组成的的字符串,第\(i\)位的字符表示第\(i\)个数和第\( ...

  2. AtCoder Grand Contest 040 简要题解

    从这里开始 比赛目录 A < B < E < D < C = F,心情简单.jpg. Problem A >< 把峰谷都设成 0. Code #include &l ...

  3. AtCoder Grand Contest 040 C - Neither AB nor BA

    传送门 好妙的题啊 首先容易想到简单容斥,统计合法方案数可以考虑总方案数减去不合法方案数 那么先考虑如何判断一个串是否合法,但是直接判断好像很不好搞 这时候就需要一些 $magic$ 了,把所有位置下 ...

  4. AtCoder Grand Contest 040 B - Two Contests

    传送门 一看就感觉很贪心 考虑左端点最右的区间 $p$ 和右端点最左的区间 $q$ 如果 $p,q$ 属于同一个集合(设为 $S$,另一个集合设为 $T$),那么其他的区间不管是不是在 $S$ 都不会 ...

  5. AtCoder Grand Contest 040 A - ><

    传送门 对于某个位置,只要知道这个位置往左最多的连续 $\text{<}$ 的数量 $x$ 和往右最多的连续 $\text{>}$ 的数量 $y$ 那么这个位置最小可能的数即为 $max( ...

  6. AtCoder Grand Contest 012

    AtCoder Grand Contest 012 A - AtCoder Group Contest 翻译 有\(3n\)个人,每一个人有一个强大值(看我的假翻译),每三个人可以分成一组,一组的强大 ...

  7. AtCoder Grand Contest 011

    AtCoder Grand Contest 011 upd:这篇咕了好久,前面几题是三周以前写的... AtCoder Grand Contest 011 A - Airport Bus 翻译 有\( ...

  8. AtCoder Grand Contest 031 简要题解

    AtCoder Grand Contest 031 Atcoder A - Colorful Subsequence description 求\(s\)中本质不同子序列的个数模\(10^9+7\). ...

  9. AtCoder Grand Contest 010

    AtCoder Grand Contest 010 A - Addition 翻译 黑板上写了\(n\)个正整数,每次会擦去两个奇偶性相同的数,然后把他们的和写会到黑板上,问最终能否只剩下一个数. 题 ...

随机推荐

  1. CSRF漏洞原理浅谈

    CSRF漏洞原理浅谈 By : Mirror王宇阳 E-mail : mirrorwangyuyang@gmail.com 笔者并未深挖过CSRF,内容居多是参考<Web安全深度剖析>.& ...

  2. HIFIMAN TWS600A听感小记——测评出街Man

    HIFIMAN TWS600A听感小记——测评出街Man 几年前蓝牙耳塞在大哥苹果的带领下有着掀翻小池塘的气势.蓝牙耳塞完全替代了我在通勤路上用的有线塞子,当时随便买了一副两百多元的塞子,低音轰头就算 ...

  3. dedecmsV5.7 后台上传m4a的音频之后不展示

    问题:dedecmsV5.7 在后台上传了m4a的音频文件(如何添加m4a的音频格式,更改系统-系统基本配置-附件设置)之后,列表里不展示,如图: 解决方案: 打开include/dialog/sel ...

  4. 打通 DevOps 任督二脉 ,CODING 2.0 制品库全新上线

    CODING 在近期的 KubeCon 2019 大会上发布了 CODING 2.0,同时发布了最新功能--制品库.CODING 不断完善 DevOps 工具链,旨在持续提升研发组织软件交付的速度与质 ...

  5. 基于Python和Xtrbackup的自动化备份与还原实现

    xtrabackup是一个MySQL备份还原的常用工具,实际使用过程应该都是shell或者python封装的自动化脚本,尤其是备份.对还原来说,对于基于完整和增量备份的还原,还原差异备份需要指定增量备 ...

  6. Nginx 简介与安装、常用的命令和配置文件

    1.nginx 简介(1)介绍 nginx 的应用场景和具体可以做什么事情 (2)介绍什么是反向代理 (3)介绍什么是负载均衡 (4)介绍什么是动静分离 2.nginx 安装(1)介绍 nginx 在 ...

  7. JS原型链与instanceof底层原理

    一.问题: instanceof 可以判断一个引用是否属于某构造函数: 另外,还可以在继承关系中用来判断一个实例是否属于它的父类型. 老师说:instanceof的判断逻辑是: 从当前引用的proto ...

  8. sass参考手册

    http://sass.bootcss.com/docs/sass-reference/

  9. pyenv环境部署

    pyenv环境部署pyenv安装使用git[root@kang ~]# yum install git -y python安装依赖yum -y install gcc make patch gdbm- ...

  10. 一篇文章看懂angularjs component组件

     壹 ❀ 引 我在 angularjs 一篇文章看懂自定义指令directive 一文中详细介绍了directive基本用法与完整属性介绍.directive是个很神奇的存在,你可以不设置templa ...