UESTC 1712 Easy Problem With Numbers 除法对和数取模,分解,线段树

2024-08-26 13:58

本文主要是介绍UESTC 1712 Easy Problem With Numbers 除法对和数取模,分解,线段树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

附上神牛原版思路:

如果这个题只有乘法,那么你肯定会做吧?线段树更新区间查找区间。
那么有除法呢?当一个数x和m互质的时候,除以x可以改为乘以x的逆元。(至于互质的数求逆元用扩展欧几里德,这个网上可以随便找到)
但是这题并不能保证除的数与m互质吧?什么时候x与m不互质呢?就是x与m含有公因子吧?
那么我们一开始就把m分解,分解出来m有p1,p2,p3,p4...pn等一些因子。
那么在这个题里面,每个数x都可以表示成p1^c1*p2^c2*p3^c3.....*pn^cn*A。这里A就是x去掉p1,p2,p3.。。等因子后的数,A与m就不含公因子了吧,就可以换成A与m的逆元。线段树里面呢,就维护c1,c2,c3.....和A的值。乘或除的时候c1,c2.。。就相应加或减。A就直接乘逆元就行了。其实就是一个线段树懒操作了,更新区间查找区间。
另外这里求逆元必须用扩展欧几里得,费马小定理只适用于mod为质数。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define debug puts("======")
typedef long long ll;
using namespace std;
const int maxn=10005;
int n,q;
int tmp;
ll m;
char str[10];
int p[50];
int cnt;
struct Node
{int c[11];ll a;
}node[maxn<<2];
Node add[maxn<<2];
int no[maxn<<2];
void pre(int u)
{cnt=0;for(int i=2;i*i<=u;i++) {if(u%i==0)p[cnt++]=i;while(u&&u%i==0) {u/=i;}}if(u>1)p[cnt++]=u;
}
ll powMod(ll a,ll b)
{if(b==0) return 1;ll ans=powMod(a,b/2);ans=ans*ans%m;if(b&1)ans=ans*a%m;return ans;
}
ll ExGcd(ll a, ll b, ll &x, ll &y)
{if(b == 0){x = 1;y = 0;return a;}ll r = ExGcd(b, a % b, x, y);ll t = x;x = y;y = t - a / b * y;return r;
}
void pushUp(int rt)
{node[rt].a=node[rt<<1].a*node[rt<<1|1].a%m;for(int i=0;i<cnt;i++) {node[rt].c[i]=node[rt<<1].c[i]+node[rt<<1|1].c[i];}
}
void build(int l,int r,int rt)
{no[rt]=0;add[rt].a=1;for(int i=0;i<cnt;i++)add[rt].c[i]=0;if(l==r) {scanf("%d",&tmp);for(int i=0;i<cnt;i++) {node[rt].c[i]=0;while(tmp && tmp%p[i]==0) {tmp/=p[i];node[rt].c[i]++;}}node[rt].a=tmp;return;}int m=((l+r)>>1);build(l,m,rt<<1);build(m+1,r,rt<<1|1);pushUp(rt);
}
void pushDown(int l,int r,int rt)
{if(no[rt]==0) return;no[rt]=0;int mm=((l+r)>>1);for(int i=0;i<cnt;i++) {add[rt<<1].c[i]+=add[rt].c[i];add[rt<<1|1].c[i]+=add[rt].c[i];node[rt<<1].c[i]+=add[rt].c[i]*(mm-l+1);node[rt<<1|1].c[i]+=add[rt].c[i]*(r-mm);add[rt].c[i]=0;}add[rt<<1].a=add[rt<<1].a*add[rt].a%m;add[rt<<1|1].a=add[rt<<1|1].a*add[rt].a%m;node[rt<<1].a=node[rt<<1].a*powMod(add[rt].a,mm-l+1)%m;node[rt<<1|1].a=node[rt<<1|1].a*powMod(add[rt].a,r-mm)%m;add[rt].a=1;no[rt<<1]=no[rt<<1|1]=1;
}
int num[20];
void update(int L,int R,int c,int l,int r,int rt,int ty)
{if(L<=l&&R>=r) {int u=c;for(int i=0;i<cnt;i++) {num[i]=0;// debug;while(u && u%p[i]==0) {u/=p[i];num[i]++;//  debug;}}// debug;no[rt]=1;if(ty==0) {for(int i=0;i<cnt;i++) {add[rt].c[i]+=num[i];node[rt].c[i]+=num[i]*(r-l+1);}add[rt].a=add[rt].a*u%m;node[rt].a=node[rt].a*powMod(u,r-l+1)%m;}else {for(int i=0;i<cnt;i++) {add[rt].c[i]-=num[i];node[rt].c[i]-=num[i]*(r-l+1);}ll px,py;ExGcd(u, m, px, py); //求u的逆元,px为逆元px%=m;if(px<0) px+=m;add[rt].a=add[rt].a*px%m;node[rt].a=node[rt].a*powMod(px,r-l+1)%m;}return;}pushDown(l,r,rt);int m=(l+r)>>1;if(L<=m)update(L,R,c,l,m,rt<<1,ty);if(R>m)update(L,R,c,m+1,r,rt<<1|1,ty);pushUp(rt);
}
ll query(int L,int R,int l,int r,int rt)
{if(L<=l&&R>=r) {ll ans=1;for(int i=0;i<cnt;i++) {ans=ans*powMod(p[i],node[rt].c[i])%m;}ans=ans*node[rt].a%m;return ans;}pushDown(l,r,rt);ll ans=1;int mm=(l+r)>>1;if(L<=mm)ans=ans*query(L,R,l,mm,rt<<1)%m;if(R>mm)ans=ans*query(L,R,mm+1,r,rt<<1|1)%m;return ans;
}
int main()
{int t;int a,b,c;int cas=1;scanf("%d",&t);while(t--) {scanf("%d%lld",&n,&m);pre(m);build(1,n,1);scanf("%d",&q);printf("Case #%d:\n",cas++);while(q--) {scanf("%s",str);if(str[0]=='M') {scanf("%d%d%d",&a,&b,&c);update(a,b,c,1,n,1,0);}else if(str[0]=='D') {scanf("%d%d%d",&a,&b,&c);update(a,b,c,1,n,1,1);}else {scanf("%d%d",&a,&b);printf("%lld\n",query(a,b,1,n,1));}}}return 0;
}


这篇关于UESTC 1712 Easy Problem With Numbers 除法对和数取模,分解,线段树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

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

uva 10025 The ? 1 ? 2 ? ... ? n = k problem(数学)

题意是    ?  1  ?  2  ?  ...  ?  n = k 式子中给k,? 处可以填 + 也可以填 - ,问最小满足条件的n。 e.g k = 12  - 1 + 2 + 3 + 4 + 5 + 6 - 7 = 12 with n = 7。 先给证明,令 S(n) = 1 + 2 + 3 + 4 + 5 + .... + n 暴搜n,搜出当 S(n) >=

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

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 ;