首页 > 其他分享 >AtCoder ABC295 D - Three Days Ago

AtCoder ABC295 D - Three Days Ago

时间:2023-04-07 21:24:04浏览次数:55  
标签:Ago AtCoder 状态 int 压缩 元素 Three str include

AtCoder ABC295 D - Three Days Ago

题目描述

给出一个数字串,问有多少子段满足,可以以某种方式将这个子段重排,将子段分成两个完全相同的部分。

样例输入输出

20230322
4

\((1, 6) (1, 8) (2, 7) (7, 8)\) 都可以满足条件

分析

如果要满足某一个字段可以被分为两个相同的部分,则不难发现必须要让这个字段中的所有字符的数量都是偶数。

暴力 \(\Theta(n^3)\)

暴力不做说明。

for(int i=1; i<=n; i++){
	for(int j=i; j<=n; j++){
		memset(tmp, 0, sizeof tmp);
		flag = false;
		
		for(int k=i; k<=j; k++){
			tmp[a[k]]++;
		}
		for(int k=0; k<10; k++){
			if(tmp[k] % 2){
				flag = true;
				break;
			}
		}
		
		if(!flag) res++;
	}
}

前缀和 \(\Theta(n^2)\)

我们可以统计一个前缀和数组 \(s_{i, j}\),表示前 \(i\) 个元素中 \(j\) 元素出现了多少次。那么,枚举所有的 \(l\) 和 \(r\),只需要判断是否对于所有的 \(j \in [0, 9]\),都满足 \(s_{r, j} - s_{l-1, j}\) 为偶数即可。

#include <iostream>
#include <cstring>

using namespace std;

const int N = 5e5 + 10;

char str[N];
int n, res, a[N];
int s[N][20];		// s[i][j] 表示前 i 个数中 j 出现的次数 

void input(){
	cin >> str + 1;
	n = strlen(str + 1);
	
	for(int i=1; i<=n; i++){
		a[i] = str[i] - '0';
	}
}

// 判断是否 [l, r] 中每个数字都出现了偶数次 
bool check(int l, int r){
	for(int i=0; i<10; i++){
		if((s[r][i] - s[l-1][i]) % 2){
			return false;
		}
	}
	return true;
}

signed main(){
	input();
	
	// 处理前缀和 
	for(int i=1; i<=n; i++){
		s[i][a[i]]++;
		for(int j=0; j<10; j++){
			s[i][j] += s[i-1][j];
		}
	}
	
	for(int i=1; i<=n; i++){
		for(int j=0; j<10; j++){
			cout << s[i][j] << ' ';
		}
		cout << '\n';
	}
	
	for(int i=1; i<=n; i++){
		for(int j=i; j<=n; j++){
			if(check(i, j)){
				res++;
			}
		}
	} 
	
	cout << res;
	
	return 0;
}

状态压缩 \(\Theta(n)\)

状态压缩

延续前缀和的思路,如果把上述代码中的 \(tmp_i\) 数组进行状态压缩,那么可以再省掉判断的时间。在这里我们规定 \(f_i\) 为前 \(i\) 个元素压缩后的状态。

那么如何把 \(10\) 个状态压缩呢?不难想到使用二进制数表示。如果二进制表示下 \(f_i\) 的第 \(j\) 位上的数是 \(1\),则代表前 \(i\) 个数中存在奇数个 \(j\)。反之,如果二进制表示下 \(f_i\) 的第 \(j\) 位上的数是 \(0\),则代表前 \(i\) 个数中存在偶数个 \(j\)。

那么,我们这样就可以把所有的状态进行压缩。代码如下:

for(int i=1; i<=n; i++){
	// 如果原来这一位是 1 
	if(f[i-1] >> a[i] & 1){
		// 变为 0 
		f[i] = f[i-1] - (1 << a[i]);
	}
	else{		// 否则变为 1 
		f[i] = f[i-1] + (1 << a[i]);
	}
}

统计完所有的状态后,就需要根据所有的状态进行计算了。

求解答案

同余:如果 \(a\),\(b\) 和 \(p\) 皆为正整数 ,且 \(a \equiv b \pmod p\),那么有 \(p \mid a-b\)。

把多个元素的状态压缩后,如果存在 \(f_i = f_j\),就说明前 \(i\) 个元素出现每个数的次数与前 \(j\) 个元素出现每个数的次数的奇偶性是相同的。

如果 \(f_i = f_j = (0000000010)_2\),就说明前 \(i\) 项和前 \(j\) 项都出现了奇数个 \(1\)。换言之,前 \(i\) 项和前 \(j\) 项出现 \(1\) 的次数对 \(2\) 取余都是 \(1\),那么根据同余的性质,不难推出前 \(i\) 项和前 \(j\) 项出现 \(1\) 的次数之差能被 \(2\) 整除,也就是 \(i+1\) 到 \(j\) 中出现了偶数个 \(1\)。

至此,我们可以推出,如果存在两个压缩后的状态相同,就会多一个满足条件的区间。如果有 \(x\) 个相同的状态,那么其中任意两个都可以组成一个合法区间,那么如果规定某一种状态出现了 \(x\) 次,那么由这种状态构成的合法区间的个数就有 \(C_{x}^{2}\) 个,即 \(\dfrac{x \cdot (x-1)}{2}\)。

for(int i=1; i<=n; i++){
	// 如果原来这一位是 1 
	if(f[i-1] >> a[i] & 1){
		// 变为 0 
		f[i] = f[i-1] - (1 << a[i]);
	}
	else{		// 否则变为 1 
		f[i] = f[i-1] + (1 << a[i]);
	}
	// 表示 f[i] 这种状态有多存在了一次 
	um[f[i]]++;
}


// 枚举每一种状态 
for(auto e: um){
	// 求组合数 C(num, 2) 
	int num = e.second;
	res += num * (num - 1) / 2;
}

完整代码

#include <iostream>
#include <cstring>
#include <unordered_map>

using namespace std;

const int N = 5e5 + 10;

int n, res, a[N];
char str[N];
int f[N];		// f[i] 表示前 i 个元素的压缩状态 

unordered_map<int, int> um;

void input(){
	cin >> str + 1;
	n = strlen(str + 1);
	
	for(int i=1; i<=n; i++){
		a[i] = str[i] - '0';
	}
}

signed main(){
	input();
	
	// 前 0 个元素状态为 0 
	f[0] = 0;
	um[f[0]]++;
	
	for(int i=1; i<=n; i++){
		// 如果原来这一位是 1 
		if(f[i-1] >> a[i] & 1){
			// 变为 0 
			f[i] = f[i-1] - (1 << a[i]);
		}
		else{		// 否则变为 1 
			f[i] = f[i-1] + (1 << a[i]);
		}
		// 表示 f[i] 这种状态有多存在了一次 
		um[f[i]]++;
	}
	
	// 枚举每一种状态 
	for(auto e: um){
		// 求组合数 C(num, 2) 
		int num = e.second;
		res += num * (num - 1) / 2;
	}
	
	cout << res;
	
	return 0;
}

标签:Ago,AtCoder,状态,int,压缩,元素,Three,str,include
From: https://www.cnblogs.com/2huk/p/17297383.html

相关文章

  • AtCoder ABC294 F - Sugar Water 2
    AtCoderABC294F-SugarWater2题意有\(2\)排糖和水。第\(1\)排有\(N\)瓶糖和\(N\)瓶水。糖分别有\(A_i\)克,水分别有\(B_i\)克。第\(2\)排有\(M\)瓶糖和\(M\)瓶水,糖分别有\(C_i\)克,水分别有\(D_i\)克。若要从第\(1\)排糖水中找到\(A_i\)克糖和......
  • threejs_交互_鼠标点击_添加物体_删除物体
    click点击添加物体,shirft+click点击删除物体<!DOCTYPEhtml><htmllang="en"><head> <metacharset="utf-8"> <title>three.jswebgl-interactive-voxelpainter</title> <metaname="viewport"conten......
  • AtCoder Beginner Contest 226(E,F,G)
    AtCoderBeginnerContest226(E,F,G)E(并查集)E这个题的大意是给我们一个无向图,我们可以把这些无向边变成有向边,让每一个点的入度都是\(1\),问有多少种变化方式要让有\(x\)个点的无向图,形成一棵树的边的数量是\(x-1\),但是我们需要的是每一个点的入度为\(1\),那么我们只还需要一条......
  • Google 投资 Lyft 背后、AlphaGo Zero 人工智能威胁论?
    阅读文本大概需要1分钟。这两天有几个新闻,今天周六,闲来没事,给大家解读下吧:1. 先说下Google投资Lyft10亿美金这事,Lyft也是类似Uber的打车软件,你可以理解成当初的滴滴和快的,我去参加IO大会期间,说实话,我们大部分都是用的Lyft,因为便宜。可能很多人不知道,Google旗下无人......
  • python安装g2opy与pagolin踩坑记录
    0x00.前言本文是在python环境下跑slam时配置环境的一点记录,感谢代码作者uoip的贡献项目代码:g2opy:https://github.com/uoip/g2opypangolin:https://github.com/uoip/pangolin0x01.安装笔者的环境是使用anaconda搭建的虚拟环境,由于一开始没有激活虚拟环境导致踩坑,之后虽然......
  • AtCoder Regular Contest 158 D - Equation
    题目链接原本看着式子直接晕了,觉得是高深的硬核数论,于是放弃(然后E也没想出来,sad)关键的思路在于,考虑构造由(a,b,c)->(ta,tb,tc)这样的求解方式。在看到这个做法后,会发现它很好地利用了题目齐次的性质;至于如何由齐次式想到这个做法,可能需要足够的天赋或者经验吧(悲)化简后得到\(At......
  • Three.js 进阶之旅:全景漫游-初阶移动相机版
    Three.js进阶之旅:全景漫游-初阶移动相机版 声明:本文涉及图文和模型素材仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。摘要3D 全景技术可以实现日常生活中的很多功能需求,比如地图的街景全景模式、数字展厅、在线看房、社交......
  • threejs 拖拽 画矩形
    import*asTHREEfrom"three";import{OrbitControls}from"three/examples/jsm/controls/OrbitControls";exportfunctioninitThree(){THREE.Object3D.DefaultUp.set(0,0,1);varscene=newTHREE.Scene();varcamera=newTHR......
  • AtCoder Beginner Contest 296
    AtCoderBeginnerContest296比赛连接好久没写题解了~~D-M<=ab题意就是给定N,M,求一个最小的数x同时满足x>=M且x=a*b(a<=N,b<=N);N,M<=1e12开始脑瘫想了二分,仔细一想很明显x不满足单调性,想了下暴力的时间复杂度巨大...纠结了一会,发现因子最大是sqrt(m),只需要枚举一下因......
  • AtCoder Beginner Contest 144
    AtCoderBeginnerContest144https://atcoder.jp/contests/abc144补一下3.23做的。D-WaterBottle分类讨论,三角函数。#include<bits/stdc++.h>#definepiacos(-1)usingnamespacestd;intmain(){inta,b,x;cin>>a>>b>>x;......