[YNOI2017]由乃的商场之旅 莫队

Description
给你一个字符串,每次给一个询问,问这个区间内有多少个子串经过重新排序后可以变成一个回文串。


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


考虑莫队,因为字符只有26个考虑把他压成二进制。
那么你就可以枚举每个位不一样,统计答案。
然后其实状态数是有限的,于是你预处理一下即可。。。


#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
struct hh {
	int x, id;
} s[61000];
struct node {
	int l, r, id, bl;
} q[61000]; int m;
int sl[61000], sr[61000];
int sum, ans[61000], S[61000], oo[61000], hg[61000][27];
char ss[61000];

bool cmp(node a, node b) {
	if(a.bl == b.bl) return a.r < b.r;
	return a.bl < b.bl;
}

bool cmp1(hh a, hh b) {return a.x < b.x;}

void jiar(int x) {
	int y = S[x];
	sl[S[x - 1]]++;
	sr[y]++;
	sum += sl[y];
	for(int i = 1; i <= hg[x][0]; i++) sum += sl[hg[x][i]];
}

void jianr(int x) {
	int y = S[x];
	sr[y]--;
	sum -= sl[y];
	for(int i = 1; i <= hg[x][0]; i++) sum -= sl[hg[x][i]];
	sl[S[x - 1]]--;
}

void jial(int x) {
	int y = S[x - 1];
	sl[y]++;
	sr[S[x]]++;
	sum += sr[y];
	for(int i = 1; i <= hg[x - 1][0]; i++) sum += sr[hg[x - 1][i]];
}

void jianl(int x) {
	int y = S[x - 1];
	sl[y]--;
	sum -= sr[y];
	for(int i = 1; i <= hg[x - 1][0]; i++) sum -= sr[hg[x - 1][i]];
	sr[S[x]]--;
}

int main() {
	int n = read(), m = read();
	scanf("%s", ss + 1); int p = sqrt(m);
	for(int i = 1; i <= n; i++) {
		int o = ss[i] - 'a'; s[i].id = i;
		s[i].x = s[i - 1].x ^ (1 << o);
	} s[n + 1].x = 0; s[n + 1].id = 0;
	sort(s + 1, s + n + 2, cmp1);
	int tp = 0;
	for(int i = 1; i <= n + 1; i++) {
		if(s[i].x != s[i - 1].x || i == 1) tp++;
		S[s[i].id] = tp; oo[tp] = s[i].x;
	}
	for(int i = 0; i <= n; i++) {
		int hh = oo[S[i]];
		for(int j = 0; j < 26; j++) {
			int yy = hh ^ (1 << j);
			int l = 1, r = n + 1, ans;
			while(l <= r) {
				int mid = (l + r) / 2;
				if(s[mid].x <= yy) l = mid + 1, ans = mid;
				else r = mid - 1;
			} if(s[ans].x == yy) hg[i][++hg[i][0]] = S[s[ans].id];
		}
	}
	for(int i = 1; i <= m; i++) {
		q[i].l = read(), q[i].r = read(), q[i].id = i;
		q[i].bl = (q[i].l - 1) / p + 1;
	} sort(q + 1, q + m + 1, cmp);
	int ll = 1, rr = 0; sum = 0;
	for(int i = 1; i <= m; i++) {
		for(int j = rr + 1; j <= q[i].r; j++) jiar(j);
		for(int j = rr; j > q[i].r; j--) jianr(j);
		for(int j = ll; j < q[i].l; j++) jianl(j);
		for(int j = ll - 1; j >= q[i].l; j--) jial(j);
		ll = q[i].l, rr = q[i].r;
		ans[q[i].id] = sum;
	} for(int i = 1; i <= m; i++) printf("%d\n", 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、付费专栏及课程。

余额充值