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. Kotlin exception

    cannot generate view binders java.lang.StackOverflowError 最近写kotlin项目,使用databinding,在适配器中定义了事件接口,在适配 ...

  2. Scrum Framework, Process and Story Point

    Scrum Framework Roles Ceremonies Artifacts Scrum Master Sprint Planning Product Backlog Product Owne ...

  3. windows下安装了2个python,如何下载模块到不同的python中

    修改python名称即可,修改Scrpit下的pip名称即可,用不同的名称打开就行 https://www.cnblogs.com/legend-123/p/11195706.html

  4. web项目的初始搭建和intellij的tomcat的配置

    点击web application

  5. August 18th, 2019. Week 34th, Sunday

    Fear doesn't shut you down, it wakes you up. 恐惧不会消磨你的意志,它能激发你的潜能. We all know that fear is powerful, ...

  6. 14. java面向对象 - 基础

    一.面向对象主线 1. Java类及类的成员:属性.方法.构造器.代码块.内部类 2. 面向对象三大特征:封装.继承.多态.(抽象性) 3. 其他关键字:this.super.static.final ...

  7. Attention 和self-attention

    1.Attention 最先出自于Bengio团队一篇论文:NEURAL MACHINE TRANSLATION BY JOINTLY LEARNING TO ALIGN AND TRANSLATE ...

  8. 【RTOS】为H7配套的uCOS-III模板也是可以用于MDK AC6的,提供个模板

    AC6模板 链接:https://pan.baidu.com/s/1_4z_Lg51jMT87RrRM6Qs3g   提取码:2gns 原始的这个模板直接修改为AC6: 编译有几十处警告,修改下面三个 ...

  9. 【ST开发板评测】使用Python来开发STM32F411

    前言 板子申请了也有一段时间了,也快到评测截止时间了,想着做点有意思的东西,正好前一段时间看到过可以在MCU上移植MicroPython的示例,就自己尝试一下,记录移植过程. MicroPython是 ...

  10. CentOS7升级OpenSSL版本

    1.CentOS7.6默认安装的openssl版本为 # 查看openssl版本 openssl version 2.下载最新的openssl wget https://www.openssl.org ...