New Year Concert (性质+二分+st表)

New Year Concert

[Link](Problem - D - Codeforces)

题意

给你一个序列你可以将更改其中的数变成任意一个数,问你最少更改几个数使得该序列的任意一个子区间的 g c d gcd gcd都不等于该区间长度。

思路

首先我们从左到右来看这个序列,按照以每个位置结尾将所有的子区间划分,发现更改其实就是将这些区间弄了个墙,比如枚举到了第 i i i个位置那么假设上次更改的位置是 x x x这次我们只需要找 x + 1 , i x+1,i x+1,i这个区间内的以 i i i结尾的区间是否存在不满足的即可,因为如果在 x x x前面到 i i i有个不合法的区间我们通过更改 x x x的值将这个区间变得合法,直觉上来想一定是区间越长 g c d gcd gcd越小,所以靠率二分 x + 1 , i x+1,i x+1,i是否存在等于长度的 g c d gcd gcd如果存在就直接在 i i i更改即可,查询区间 g c d gcd gcd可以离线 s t st st与处理一下。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
	e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int f[N][20];
int find(int l, int r) {
	if (l > r) return 0;
	int j = log2(r - l + 1);
	return __gcd(f[l][j], f[r - (1 << j) + 1][j]);
}
int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n;
	
	for (int i = 1; i <= n; i ++) cin >> f[i][0];
	for (int j = 1; j <= 19; j ++)
		for (int i = 1; i + (1 << j) - 1 <= n; i ++) 
			f[i][j] = __gcd(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);

	int res = 0;
	for (int i = 1, j = 0; i <= n; i ++) {
		int l = j + 1, r = i; 
		while (l < r) {
			int mid = l + r >> 1;
			if (find(mid, i) >= i - mid + 1) r = mid;
			else l = mid + 1;
		}

	
		if (find(l, i) == i - l + 1) 
			res ++, j = i;
			
		cout << res << ' ';
	}
	
	return 0;
}

再进一步思考发现,其实还有区间 g c d gcd gcd还有一些性质,我们以 i i i为起点的区间使得区间长度等于 g c d gcd gcd的区间最多只有一个,假设 g c d ( i , . . . , j ) = j − i + 1 gcd(i,...,j)=j-i+1 gcd(i,...,j)=ji+1那么再往外扩展一定不会使 g c d gcd gcd变大,而往左减少长度不会使 g c d gcd gcd变小,因此以某个点为起点和以每个点为终点的 g c d gcd gcd最多只有一个,因此我们可以先从左往右二分预处理出来每个唯一的区间,然后对于这些区间做一下类似于区间合并算贡献。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <unordered_map>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
	e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int f[N][20];
int g[N];
int find(int l, int r) {
	if (l > r) return 0;
	int j = log2(r - l + 1);
	return __gcd(f[l][j], f[r - (1 << j) + 1][j]);
}
int main() {
	ios::sync_with_stdio(false), cin.tie(0);
	cin >> n;
	
	for (int i = 1; i <= n; i ++) cin >> f[i][0];
	for (int j = 1; j <= 19; j ++)
		for (int i = 1; i + (1 << j) - 1 <= n; i ++) 
			f[i][j] = __gcd(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);

	for (int i = 1; i <= n; i ++) {
		int l = i, r = n;
		while (l < r) {
			int mid = l + r >> 1;
			if (find(i, mid) <= mid - i + 1) r = mid;
			else l = mid + 1;
		}
		if (find(i, l) == l - i + 1) g[l] = i;
	}
	
	int res = 0;
	for (int i = 1, j = 0; i <= n; i ++) {
		if (j < g[i]) res ++, j = i;
		
		cout << res << ' ';
	}
		
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值