zoukankan      html  css  js  c++  java
  • 20180711模拟赛T3——聚变

    文件名: fusion
    题目类型: 传统题
    时间限制: 3秒
    内存限制: 256MB
    编译优化:

    题目描述

    知名科学家小A在2118年在计算机上实现了模拟聚变的过程。
    我们将她研究的过程简化。
    核子共有26种,可以用a到z共26个字母表示。
    核子聚变的过程可以用一个字符串描述。
    按照顺序从左到右的顺序,假如有两个相同核子相邻,两个核子就会相互吸引发生聚变生成一个序号+1的核子,特殊的,两个z核子相邻会湮灭没有新的核子生成。
    每当两个核子聚变时,就需要重新从左到右重复找到两个相邻的相同核子直到不存在为止。
    比如zyzzy->zyy->zz->
    小A为了做出足够有效的实验,每次会从一个字符串中选定一个子串操作。
    她想要知道每次实验这个子串中的核子能否最终全部湮灭。

    输入格式

    第一行一个只有小写字母的字符串。
    第二行一个数(n)表示询问次数
    接下来(n)行每行两个正整数(l_{i},r_{i})表示询问区间

    输出格式

    对每次询问输出一行Yes或No表示答案

    样例输入

    yzyyyzyzyyyz
    8
    1 6
    7 12
    1 12
    6 11
    1 1
    1 3
    4 9
    3 8

    样例输出

    Yes
    Yes
    Yes
    Yes
    No
    No
    No
    No

    数据规模与约定

    L表示字符串长度
    对于30%的数据满足L<=100
    对于60%的数据满足L<=3000,n<=3000
    另存在20%数据满足字符串中只存在y,z
    对于100%的数据,L<=500000,n<=1000000

    题解

    开始没看到“从左往右”,还以为是线段树维护分治……

    (nL)大力60。

    我们发现对于一段合法串,我们可以把它分成多个(或一个)连续的合法字串。

    比如:

    zyyzyyyyz
    

    我们可以分成:

    zyy | zyy | yyz
    

    这三段都是合法的,所以原串也是合法的。

    又比如说:

    zyyyyzyz
    

    我们把它分成:

    zyy | yyz | yz
    

    显然,yz是不合法的,所以原串是不合法的。

    于是我们检验时,我们可以让指针跳着走:

    zyy | yyz | yz
    ^
    zyy | yyz | yz
    ---->^
    zyy | yzz | yz
          ---->^
    zyy | yzz | yz
                ---->^
    

    发现指针跳到外面去了,所以说是不合法的。

    不难想到用一个nxt数组表示以第i个开始的最短合法串末尾的下一个位置(感觉很像kmp)。

    那么怎么求nxt呢?

    我们需要引入一个to数组。

    to[i][j]表示从第i位开始最短能拼成j字符的位置的下一个位置((j = 0sim 26)(0)表示(a)(26)表示没有)。

    不难发现to[i][j] = to[to[i][j-1]][j-1]nxt[i] = to[i][26]

    当然,开始时tonxt都指向结尾的后面。

    然而我们发现这样做会被hack:

    xzzxyz

    这是因为对于第一个z来讲它只更新了(25sim 26)的情况,所以第一个第一个xnxt会指向末尾。

    所以我们需要排除中间这段zz的干扰。

    于是我们又想到转移方程:to[i][j] = to[nxt[i]][j]

    这样就大功告成了。

    然而这样做很容易被卡掉,比如说全是z的情况,往后跳的速度会很慢。

    所以我们选择倍增。

    下面是代码:

    #include <cstdio>
    #include <cstring>
    
    const int maxn = 500005;
    
    char aa[maxn];
    int nxt[maxn][21];
    int to[maxn][26];
    
    inline void pre()
    {
    	int len = strlen(aa);
    	for(int i = 0; i < len; ++i)
    		aa[i] -= 'a';
    	for(int i = 0; i < len + 3; ++i)
    	{
    		for(int j = 0; j <= 20; ++j)
    			nxt[i][j] = len + 1;
    		for(int j = 0; j <= 26; ++j)
    			to[i][j] = len + 1;
    	}
    	for(int i = len - 1; i >= 0; --i)
    	{
    		to[i][(int)aa[i]] = i + 1;
    		for(int j = aa[i] + 1; j <= 26; ++j)
    			to[i][j] = to[to[i][j-1]][j-1];
    		nxt[i][0] = to[i][26];
    		for(int j = 1; j <= 20; ++j)
    			nxt[i][j] = nxt[nxt[i][j-1]][j-1];
    		for(int j = 0; j < 26; ++j)
    			if(to[i][j] == len + 1)
    				to[i][j] = to[nxt[i][0]][j];
    	}
    }
    
    inline bool pan(int l, int r)
    {
    	l--;
    	r--; // 从0开始……(感觉自己好作死)
    	for(int i = 20; i >= 0; --i)
    	{
    		if(nxt[l][i] <= r + 1)
    			l = nxt[l][i];
    		if(l == r + 1)
    			return true;
    	}
    	return false;
    }
    
    int main()
    {
    	freopen("fusion.in", "r", stdin);
    	freopen("fusion.out", "w", stdout);
    	gets(aa);
    	pre();
    	int n;
    	scanf("%d", &n);
    	while(n--)
    	{
    		int l, r;
    		scanf("%d%d", &l, &r);
    		puts(pan(l, r) ? "Yes" : "No");
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    php中获取各种路径
    大型网站系统架构演化之路
    404、500、502等HTTP状态码介绍
    Linux 查看进程和删除进程
    mysql中FIND_IN_SET的使用方法
    PHP导出Excel 数字末尾变0或小数点解决办法
    PHP API接口测试小工具
    要慎用mysql的enum字段的原因
    mysql 导入&导出sql文件
    Linux下php安装memcache扩展
  • 原文地址:https://www.cnblogs.com/pfypfy/p/9312200.html
Copyright © 2011-2022 走看看