• 【做题】atc_cf17-final_E


    题意:给出一个由26个小写字母组成的字符串,可以任意地进行若干个操作,每次操作是让指定区间内的字母变为下一个字母(z变成a)。问是否存在方案使得这个字符串变为回文串。

    一开始的想法是构造len/2条模26意义下方程,但由于26不是质数,判断有没有解并不容易。(下文自动省略模26)

    我们发现,一次操作对于方程组的影响大概是这样的:

    如上图所示,一次对区间[l,r]进行的操作,其中从一端到其关于中点的对称点的区间实质是没有贡献的。(上图中为区间[l,l'])同样地,对于完全位于中点右边的区间,可以将其翻转到中点左边。

    因此,所有操作的影响都被移动到了中点左边,则每一条方程都变成了如下形式:k1*x1+k2*x2+...+kn*xn=p。其中,p为常数,且ki为0或1。

    而且,每一次操作的影响都是一个区间,所以这就成了一个通过区间操作是一个序列中元素全部变为0的问题。

    上述问题简单地说就是,回文串就是左右两边的元素差值为0。所以最终目的就是使这些差值全部变为0。

    所以很容易想到对于那个需要变成0的数列差分得到序列a,每一次操作区间[l,r]就是a[l]+1,a[r+1]-1。这等价于令a[l]+a[r+1]一定,然后任意确定a[l]和a[r+1]的值。

    同样地,两个操作[l1,r-1]和[l2,r-1]也就是a[l1]+a[l2]+a[r]一定,然后任意确定这三个数的值。

    因此,我们可以建一张有len/2+1个点的图,点i的权值为a[i],每一次操作[l,r]就是给点l和点r+1连一条边。则存在方案 <=> 每个连通分量内点权和为0。

    上述过程可以用并查集实现。

    时间复杂度O(n*α(len))。(下面代码没有按秩合并,时间复杂度为O(n*logn);有代码更优美的实现方式,即计算时保留S的右半部分)

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 100010;
     4 int n,m,s[N],sum[N],dif[N];
     5 int flag[N];
     6 int get_flag(int pos) {
     7     return flag[pos] == pos ? pos : flag[pos] = get_flag(flag[pos]);
     8 }
     9 void fit(int& x) {
    10     if (x > n/2) x = n-x+1;
    11 }
    12 int fix(int x) {
    13     if (x<0) x = 26-(-x)%26;
    14     x %= 26;
    15     return x;
    16 }
    17 int main() {
    18     int l,r;
    19     for (char tmp=getchar();tmp>='a'&&tmp<='z';tmp=getchar())
    20         s[++n] = tmp-'a';
    21     for (int i=1;i<=n/2;++i)
    22         s[i] -= s[n-i+1],dif[i]=s[i]-s[i-1],flag[i]=i;
    23     dif[n/2+1] = -s[n/2];
    24     scanf("%d",&m);
    25     for (int i=1;i<=m;++i) {
    26         scanf("%d%d",&l,&r);
    27         if (n&1) {
    28             if (l == n/2+1&&r == n/2+1) continue;
    29             if (l == n/2+1) l++;
    30             if (r == n/2+1) r--;
    31         }
    32         if (l<=n/2&&r>n/2) {
    33             fit(r);
    34             if (l>r) swap(l,r);
    35             r--;
    36         } else {
    37             fit(l);fit(r);
    38             if (l>r) swap(l,r);
    39         }
    40         flag[get_flag(l)]=flag[get_flag(r+1)];
    41     }
    42     for (int i=1;i<=n/2+1;++i) sum[get_flag(i)] += dif[i];
    43     for (int i=1;i<=n/2+1;++i) if (fix(sum[i])!=0) {
    44         return 0*puts("NO");
    45     }
    46     puts("YES");
    47     return 0;
    48 }

    小结:一种重要解题方法就是对题目进行转化。

  • 相关阅读:
    写在学期将要结尾处
    程序有感
    Python字典操作小汇总
    mysql 乱码解决方案
    公告 百度安家
    本周工作安排
    bind1st/bind2nd
    OpenGL放大缩小实现
    Eclipse安装SVN插件
    QQ也玩命令行
  • 原文地址:https://www.cnblogs.com/cly-none/p/7978642.html
Copyright © 2020-2023  润新知