Codecraft-18 and Codeforces Round #458 D. Bash and a Tough Math Puzzle(线段树)

2024-01-29 12:38

本文主要是介绍Codecraft-18 and Codeforces Round #458 D. Bash and a Tough Math Puzzle(线段树),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Bash likes playing with arrays. He has an array a1, a2, ... an of n integers. He likes to guess the greatest common divisor (gcd) of different segments of the array. Of course, sometimes the guess is not correct. However, Bash will be satisfied if his guess is almost correct.

Suppose he guesses that the gcd of the elements in the range [l, r] of a is x. He considers the guess to be almost correct if he can change at most one element in the segment such that the gcd of the segment is x after making the change. Note that when he guesses, he doesn't actually change the array — he just wonders if the gcd of the segment can be made x. Apart from this, he also sometimes makes changes to the array itself.

Since he can't figure it out himself, Bash wants you to tell him which of his guesses are almost correct. Formally, you have to process qqueries of one of the following forms:

  • 1 l r x — Bash guesses that the gcd of the range [l, r] is x. Report if this guess is almost correct.
  • 2 i y — Bash sets ai to y.

Note: The array is 1-indexed.

Input

The first line contains an integer n (1 ≤ n ≤ 5·105)  — the size of the array.

The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 109)  — the elements of the array.

The third line contains an integer q (1 ≤ q ≤ 4·105)  — the number of queries.

The next q lines describe the queries and may have one of the following forms:

  • 1 l r x (1 ≤ l ≤ r ≤ n, 1 ≤ x ≤ 109).
  • 2 i y (1 ≤ i ≤ n, 1 ≤ y ≤ 109).

Guaranteed, that there is at least one query of first type.

Output

For each query of first type, output "YES" (without quotes) if Bash's guess is almost correct and "NO" (without quotes) otherwise.

Examples
input
3
2 6 3
4
1 1 2 2
1 1 3 3
2 1 9
1 1 3 2
output
YES
YES
NO
input
5
1 2 3 4 5
6
1 1 4 2
2 3 6
1 1 4 2
1 1 5 2
2 5 10
1 1 5 2
output
NO
YES
NO
YES

题意:给你一个数组,然后有两种操作,第一种是问一个区间[l,r]的gcd是否几乎是x,什么是几乎是x呢,就是改变[l,r]中最多一个数字使得区间的gcd可以为x(实际上并没有改变这个数字),第二种是改变数组中的一个数字。

思路:本来是想用线段树做区间gcd,然后如果要查询[l,r],就先判断[l,mid]和[mid+1,r]这两半区间是否最多有一个不是x的倍数,如果两个都不是x的倍数,那么就不可能改变为x;如果两个都是x的倍数,那么这一整段的gcd肯定可以是x;如果有一个不是x的倍数,那么就递归处理这一段。代码如下:

bool judge(int l,int r,int x)
{int m = (l+r)/2;int q1 = query(l,m,1,n,1);int q2 = query(m+1,r,1,n,1);if(q1 % x != 0 && q2 % x != 0)return false;if(q1 % x == 0 && q2 % x == 0)return true;if(l == m || m+1 == r)return true;if(q1 % x != 0)return judge(l,m,x);if(q2 % x != 0)return judge(m+1,r,x);
}
这种的复杂度是q乘上logn的平方,按道理来说不应该T,但是T12,然后把它改成了while形式,还有其他各种优化,终于强行怼过了样例PP了,可是还是没有逃过终测。


最后换成了直接在query查询线段树的时候就做上面的工作,这样就可以省掉一个log。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>using namespace std;int gcd(int a,int b)
{if(b == 0)return a;elsereturn gcd(b,a%b);
}
const int maxn = 500010;
int sum[maxn*4];void PushUp(int rt)
{sum[rt] = gcd(sum[rt*2],sum[rt*2+1]);
}void build(int l, int r, int rt)
{if (l == r){scanf("%d",&sum[rt]);return;}int m = (l + r) / 2;build(l, m, rt*2);build(m + 1, r, rt*2+1);PushUp(rt);
}
void update(int p,int c, int l, int r, int rt)
{if(l == r){sum[rt] = c;return;}int m = (l + r) / 2;if(p <= m)update(p, c, l, m, rt*2);elseupdate(p, c, m + 1, r, rt*2+1);PushUp(rt);
}
int cnt,x;
void query(int ll, int rr, int l, int r, int rt)
{if(cnt > 1) //记得加这句剪枝return;if(l == r){cnt++;return;}int m = (l + r) / 2;int ret = 0;if(ll <= m && sum[rt*2] % x != 0)query(ll, rr, l, m, rt*2);if(rr > m && sum[rt*2+1] % x != 0)query(ll, rr, m + 1, r, rt*2+1);
}int main(void)
{int n,q,i,j;scanf("%d",&n);build(1,n,1);scanf("%d",&q);while(q--){int op;scanf("%d",&op);if(op == 1){int l,r;scanf("%d%d%d",&l,&r,&x);cnt = 0;query(l,r,1,n,1);if(cnt <= 1)printf("YES\n");elseprintf("NO\n");}else{int p,c;scanf("%d%d",&p,&c);update(p,c,1,n,1);}}return 0;
}


这篇关于Codecraft-18 and Codeforces Round #458 D. Bash and a Tough Math Puzzle(线段树)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/656920

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

HDU4737线段树

题目大意:给定一系列数,F(i,j)表示对从ai到aj连续求或运算,(i<=j)求F(i,j)<=m的总数。 const int Max_N = 100008 ;int sum[Max_N<<2] , x[Max_N] ;int n , m ;void push_up(int t){sum[t] = sum[t<<1] | sum[t<<1|1] ;}void upd

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m