AtCoder Grand Contest 026题解

前四个题都很基础啊。

E - Synchronized Subsequence

这种思路不是很好想啊。

我们发现这个题直接逐位确定非常的难啊,根本没法做。

正解是将原序列分成了若干个段,使得$a$的个数等于$b$的个数。

并且要保证是最小的划分。

之后有一个结论就是在同一段里$a$与对应$b$的前后关系是一样的。

这个可以用不存在一种更小的划分来证,即中间不存在前缀$cnt[a]==cnt[b]$。

对于$a$在$b$i前面的段,显然分成$ababab......$最优。

对于$a$在$b$后面的段,是一个完整的后缀最优。

证明的话就是我们删掉一个前缀并选择剩下的一些点后,剩下没有选的位置都是$b$,所以一定是选上最优。

第二种我用了一个$cnt$代表开头$b$的个数还有一个串$S$来表示这个字符串。

这样就可以用后缀自动机$O(1)Lcp$来优化到$O(n)$了。

#include <bits/stdc++.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define for1(a,b,i) for(int i=a;i<=b;++i)
#define FOR2(a,b,i) for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
inline int read() {
    int f=1,sum=0;
    char x=getchar();
    for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
    for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
    return f*sum;
}

#define N 10000
#define M 1000005
int n;
char s[M];
int be[M],f[M],buc[M];
int id[M],Max[M],pos[M],nxt[M],sum[M],L[M],R[M];

inline bool check(int x,int xr,int y,int yr) {
    int len=min(xr-x,yr-y);
    for1(0,len,i)
        if(s[x+i]!=s[y+i]) return s[x+i]<s[y+i];
    return xr-x<=yr-y;
}

inline bool check_Max(int x,int y) {
    if(f[x]<N&&f[y]>N) return 0;
    if(f[x]>N&&f[y]<N) return 1;
    if(f[x]<N) return f[x]>=f[y];
    if(f[x]/N>f[y]/N||(f[x]/N==f[y]/N&&check(f[y]%N,R[y],f[x]%N,R[x]))) return 1;
    return 0;
}

int main() {
    //freopen("a.in","r",stdin);
    //freopen("mine.out","w",stdout);
    n=read()*2;
    scanf("%s",s+1);
    int shu=0;
    for1(1,n,i) {
        if(!shu) be[i]=++be[0];
        else     be[i]=be[i-1];
        shu+=('a'==s[i])-('b'==s[i]);
    }
    for1(1,n,i) R[be[i]]=i;
    FOR2(n,1,i) L[be[i]]=i;
    
    for(int l=1,r;l<=n;l=r+1) {
        r=l;
        while (be[r+1]==be[l]) ++r;
        if(s[l]=='a') {
            //怎么都没想到这里错啊,太马虎了
            int h=1,t=0;
            for1(l,r,i) 
                if(s[i]=='a') buc[++t]=i;
                else pos[buc[h++]]=i;
            int now=l;
            while (1) {
                ++f[be[l]];
                now=pos[now]+1;
                while (now<=r&&s[now]!='a') ++now;
                if(now>r) break;
            }
        }
        else {
            sum[l-1]=0;
            for1(l,r,i) sum[i]=sum[i-1]+(s[i]=='b');
            int h=1,t=0;
            for1(l,r,i) 
                if(s[i]=='b') buc[++t]=i;
                else pos[buc[h++]]=i;
            FOR2(r,l,i)
                if(s[i]=='b') nxt[i]=nxt[i+1];
                else nxt[i]=i;
            int pre=sum[pos[l]],id=pos[l];
            for1(l,r-1,i) if(s[i]=='b') {
                int x[2]={sum[pos[i]]-sum[i],pos[i]+1};
                x[0]+=nxt[x[1]]-x[1];
                x[1]=nxt[x[1]];
                if(x[0]>pre||(x[0]==pre&&check(id,r,x[1],r))) pre=x[0],id=x[1];
            }
            f[be[l]]=pre*N+id;
        }
    }
    
    //abab是累加,不能比较字典序。。
    FOR2(be[0],1,i) {
        Max[i]=Max[i+1];
        if(f[i]>N&&check_Max(i,Max[i])) Max[i]=i;
    }
    //for1(1,be[0],i) cout<<f[be[i]]<<" "; cout<<endl;
    for1(1,be[0],i) if(!Max[i]) Max[i]=i;
    int pos=0;
    while (1) {
        pos=Max[pos+1];
        if(f[pos]>N) {
            FOR2(f[pos]/N,1,i) putchar('b');
            for1(f[pos]%N,R[pos],i) putchar(s[i]);
        }
        else {
            for1(1,f[pos],i) putchar('a'),putchar('b');
        }
        if(pos==be[0]) break;
    }
    puts("");
}

F - Manju Game

$O(n^3)$是显然的,然后就想不出来了。

正解分析性质就很多了。。

首先黑白染色,$10101010......$。

若$n$是偶数,有两个结论:

1>先手答案大于等于$max(B,W)$

2>后手答案大于等于$min(B,W)$

第一个好证啊。。直接选一个边界就好了,第二个直接归纳法证一下就好了。

这样$n$为偶数就可以直接输出了。

当$n$为奇数时,第一步若我们选了黑色,依然好说。

但是选白色就比较复杂了。

我们考虑因为如果我们选白色,之后一定一直都是我们先手,这样我们就得到了一个白色点的集合。

显然我们可以在某个区间内让我们选黑色,后手选白色,其他区间我们要白色,后手选黑色。

如果二分答案之后存在一个白色点的集合使得任意一个区间都满足$B-W>=mid$即可。

这个显然是正确的。因为我们选的那个区间一定是$B-W$最小的区间,如果后手不让我们选这个区间是更亏的。

#include <bits/stdc++.h>
#define for1(a,b,i) for(int i=a;i<=b;++i)
#define FOR2(a,b,i) for(int i=a;i>=b;--i)
using namespace std;

#define M 1000005
int n;
int a[M];

inline bool check(int x) {
    int sum=0;
    for(int i=1;i<=n;i+=2) {
        if(sum>=x) sum=max(sum+a[i]-a[i-1],a[i]);
        else sum+=a[i]-a[i-1];
    }
    return sum>=x;
}

int main () {
    //freopen("a.in","r",stdin);
    scanf("%d",&n);
    for1(1,n,i) scanf("%d",a+i);
    int sum[2]={0};
    for1(1,n,i) sum[i&1]+=a[i];
    if(!(n&1)) printf("%d %d\n",max(sum[0],sum[1]),min(sum[0],sum[1]));
    else {
        int l=0,r=sum[1],mid,ans;
        while (l<=r) {
            mid=l+r>>1;
            if(check(mid)) ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d %d\n",sum[0]+ans,sum[1]-ans);
    }
}

转载于:https://www.cnblogs.com/asd123www/p/9712789.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值