4866: [Ynoi2017]由乃的商场之旅

4866: [Ynoi2017]由乃的商场之旅

Time Limit: 20 Sec Memory Limit: 164 MB
Submit: 192 Solved: 47
[Submit][Status][Discuss]
Description

由乃有一天去参加一个商场举办的游戏。商场派了一些球王排成一行。每个人面前有几堆球。说来也巧,由乃和你
一样,觉得这游戏很无聊,于是决定换一个商场。另一个商场是Deus的,他看到由乃来了,于是想出了一个更有趣
的游戏:写数据结构题这个题是这样的:
我们的情人,不过是随便借个名字,用幻想吹出来的肥皂泡,把信拿去吧,你可以使假戏成真。我本来是无病呻吟
,漫无目的的吐露爱情—现在这些漂泊不定的鸟儿有地方栖息了,你可以从信里看出来。拿去吧—由于不是出自
真心,话就说得格外动听,拿去吧,就这么办吧…由于世界会在7月20日完结,作为救世主,间宫卓司要在19日让
所有人回归天空现在已经是19日傍晚,大家集合在C栋的天台上,一共n个人在他们面前,便是终之空,那终结的天

回归天空是一件庄重的事情,所以卓司决定让大家分批次进行,给每个人给了一个小写字母’a’->’z’作为编号一个
区间的人如果满足他们的编号重排之后可以成为一个回文串,则他们可以一起回归天空,即这个区间可以回归天空
由于卓司是一个喜欢妄想的人,他妄想了m个区间,每次他想知道每个区间中有多少个子区间可以回归天空因为世
界末日要来了,所以卓司的信徒很多
由乃天天做数据结构已经快恶心死了,于是让您帮她做当然,您天天做数据结构题,肯定也觉得恶心死了,不过可
爱即正义!所以这个题还是要做的~
Input

第一行两个数n,m
之后一行一个长为n的字符串,代表每个人的编号
之后m行每行两个数l,r代表每次卓司妄想的区间
n,m<=60000
Output

m行,每行一个数表示答案
Sample Input

6 6

zzqzzq

1 6

2 4

3 4

2 3

4 5

1 1
Sample Output

16

4

2

2

3

1
HINT

Source

By 佚名提供

对于字母 a ,赋予权值1,对于字母 b ,赋予权值2,对于字母 c ,赋予权值4
那么对于原串的任意一段子区间,取 G 为区间内权值的xor
这个子区间能重新排列成一个回文串,当且仅当 G=0 G 2的幂次
用前缀后缀的思路瞎搞搞,能想出一个单次添加或删除头或尾 O(26) 的算法
结合莫队算法能搞出一个 O(26nn) 的算法
然而无论怎么卡常数,这个算法就是过不去。。。。。
没有办法的,理论复杂度太差了!
下面结合平衡规划的思路,给出一个 O(n26n) 的算法
pre[i] 为位置 i 的前缀xor
那么任意两个 pre 值的 xor 结果就能表示原串中一个子区间的信息
cnt[x] 为当新增一个值为 pre[x] 的端点时新增的合法区间个数
那么新增端点时可以 O(26) 更新好 cnt
B=26n ,对于原串,每 B 个字符分一块
对于每个位置i,记 sum[i] 为位置 i 到它所在块的右端点这个区间中的合法区间个数
那么对于所有长度大于B的询问,它的答案分三类统计

这里写图片描述

图中 p 为左端点L所在块的右端点
对于 [L,p] 内部的贡献已经预处理好了
对于 [p+1,R] 的贡献可以通过莫队算法修改的时候统计好
对于横跨两段的贡献,枚举每个 [L1,p1] pre 值,直接用当前的 cnt 即可
于是对于所有长度大于 B 的询问可以用总的O(n26n)解决
对于那些长度不超过 B 的询问,从左往右O(26n)更新 cnt 数组
在过程中利用类似上面那个办法减掉不存在的贡献即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;

const int M = 50;
const int N = 1 << 26;
const int maxn = 6E4 + 6;
typedef unsigned int u32;
typedef unsigned short u16;

struct data{
    int l,r,Num; data(){}
    data(int l,int r,int Num): l(l),r(r),Num(Num){}
    bool operator < (const data &B) const {return r < B.r;}
};

int n,m,tot,B,pre[maxn],bel[maxn],L[M],R[M],mi[30];
u16 cnt[N]; u32 sum[maxn],Ans[maxn];

vector <data> v[M],F[maxn],G[maxn];

inline void Add(int x) {for (int i = 0; i <= 26; i++) ++cnt[x ^ mi[i]];}
inline void Del(int x) {for (int i = 0; i <= 26; i++) --cnt[x ^ mi[i]];}

inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}

inline int Get()
{
    char ch = getchar();
    while (ch < 'a' || 'z' < ch) ch = getchar();
    return 1 << ch - 'a';
}

char s[20];
inline void Print(u32 x)
{
    if (!x) {puts("0"); return;} int len = 0;
    while (x) s[++len] = x % 10,x /= 10;
    for (int i = len; i; i--) putchar(s[i] + '0'); puts("");
}

void Solve1()
{
    u32 now = 0; Add(pre[0]);
    for (int i = 1; i <= n; i++)
    {
        now += cnt[pre[i]];
        for (int j = 0; j < F[i].size(); j++)
            Ans[F[i][j].Num] += now;
        for (int j = 0; j < G[i].size(); j++)
        {
            data d = G[i][j];
            for (int k = d.l; k <= d.r; k++)
                Ans[d.Num] -= cnt[pre[k]];
            Ans[d.Num] -= now;
        }
        Add(pre[i]);
    }
    for (int i = 0; i <= n; i++) Del(pre[i]);
}

void Solve2()
{
    for (int i = 1; i <= tot; i++)
    {
        if (!v[i].size()) continue;
        sort(v[i].begin(),v[i].end());
        int r = R[i]; u32 now = 0; Add(pre[R[i]]);
        for (int j = 0; j < v[i].size(); j++)
        {
            data d = v[i][j];
            while (r < d.r) now += cnt[pre[++r]],Add(pre[r]);
            Ans[d.Num] = now + sum[d.l]; Del(pre[R[i]]);
            for (int j = d.l - 1; j < R[i]; j++)
                Ans[d.Num] += cnt[pre[j]]; Add(pre[R[i]]);
        }
        for (int j = R[i]; j <= r; j++) Del(pre[j]);
    }
}

void Pre_Work()
{
    mi[0] = 1; for (int i = 1; i < 26; i++) mi[i] = mi[i - 1] << 1;
    for (int i = 1; i <= n; i++)
        pre[i] = pre[i - 1] ^ Get(),bel[i] = (i - 1) / B + 1;
    for (int i = 1; i <= bel[n]; i++)
        L[i] = R[i - 1] + 1,R[i] = i * B;
    tot = bel[n]; R[tot] = n;
    for (int i = 1; i <= tot; i++)
    {
        for (int j = R[i] - 1; j >= L[i] - 1; j--)
            Add(pre[j + 1]),sum[j + 1] = sum[j + 2] + cnt[pre[j]];
        for (int j = R[i]; j >= L[i]; j--) Del(pre[j]);
    }
    for (int i = 1; i <= m; i++)
    {
        int l = getint(),r = getint();
        if (bel[l] == bel[r])
        {
            F[r].push_back(data(l,r,i));
            G[l - 1].push_back(data(l,r,i));
        }
        else v[bel[l]].push_back(data(l,r,i));
    }
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    n = getint(); m = getint();
    B = sqrt(26 * n); Pre_Work();
    Solve1(); Solve2();
    for (int i = 1; i <= m; i++) Print(Ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TokenType.SYMBOL_LEFT_PAREN) { match(TokenType.SYMBOL_LEFT_PAREN); expression(); match(TokenType.SYMBOL_RIGHT_PAREN); } else { throw new Exception("Syntax error"); } } private void match(TokenType type) throws Exception { if (tokenList.get(index).getType() == type) { index++; } else { throw new Exception("Syntax error"); } } private int getVariableAddress(String identifier) throws Exception { if (!symbolTable.containsKey(identifier)) { 这是一道程序设计竞赛题目,需要设计算法来解决。题目描述为: 有一个大小为 $ throw new Exception("Undefined variable"); } return symbolTable.get(identifier); } } ``` 在语义分析程序n\times m$ 的网格图,每个格子中有一个整数。定义一个点 $(i,j)$ 的权中,我们首先定义了四个成员变量:词法单元列表、当前处理的词法单元下标、符号表和四元式列表。其中符号表用于存储变量名和对应的内存地址,值为 $a_{i,j}$ 与其上下左右四个点权值的和的乘积,即 $a四元式列表用于存储生成的四元式。 然后,我们定义了一系列方法来进行语义分_{i,j}\times (a_{i-1,j}+a_{i+1,j}+a_{i,j-1}析和生成四元式。这些方法分别对应PL/0语言的不同语法结构,例如程序+a_{i,j+1})$。现在你需要找到一个点 $(i,j)$,使得其权值最、块、语句、表达式、项和因子。在处理每个语法结构时,我们先判断当前大,输出这个最大权值。 需要用到搜索算法或动态规划算法来解决这个问题,具体实现可以参考题目解析。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值