【分解质因数】【树状数组】【快速幂】codeforces 2014 ACM-ICPC Vietnam National Second Round E. ACM

乘除都在150以内,分解质因数后发现只有35个,建立35个树状数组/线段树,做区间加、区间查询,最后快速幂起来。

#include<cstdio>
#include<cstring>
using namespace std;
#define N 50001
typedef long long ll;
ll Quick_Pow(ll a,ll p,ll MOD)
{
    if(!p) return 1;
    ll ans=Quick_Pow(a,p>>1,MOD);
    ans=ans*ans%MOD;
    if((p&1)==1) ans=ans*a%MOD;
    return ans;
}
int zu,n,m;
int prime[151],en,zyz[151][40];
bool vis[151];
struct BIT
{
	ll d[N];
	void add_node(int p,const ll &v)
	{for(;p<=n;p+=(p&(-p)))d[p]+=v;}
	ll query(int p)
	{ll res=0;for(;p;p-=(p&(-p)))res+=d[p];return res;}
	void clear(){memset(d,0,sizeof(d));}
}T[40];
struct BIT2
{
	ll d[N];
	void add_node(int p,const ll &v)
	{for(;p<=n;p+=(p&(-p)))d[p]+=v;}
	void add_range(const int &L,const int &R,const ll &v)
	{add_node(L,v);if(R!=n)add_node(R+1,-v);}
	ll query(int p)
	{ll res=0;for(;p;p-=(p&(-p)))res+=d[p];return res;}
	void clear(){memset(d,0,sizeof(d));}
}T2[40];
void Shai()
{
    vis[1]=1;
    for(int i=2;i<=150;i++)
      {
      	if(!vis[i]) prime[++en]=i;
        for(int j=i*i;j<=150;j+=i)
          vis[j]=1;
      }
}
void add(const int &wh,const int &p,const int &v)
{
	T[wh].add_node(p,(ll)v*(ll)p);
	if(p!=1) T2[wh].add_range(1,p-1,(ll)v);
}
void Add(const int &wh,const int &L,const int &R,const int &v)
{
	add(wh,R,v);
	if(L!=1) add(wh,L-1,(ll)(-v));
}
ll query(const int &wh,const int &p)
{
	return T[wh].query(p)+T2[wh].query(p)*(ll)p;
}
ll Query(const int &wh,const int &L,const int &R)
{
	return query(wh,R)-(L==1?0:query(wh,L-1));
}
ll QUERY(const int &wh,const int &L,const int &R)
{
	if(L<=R) return Query(wh,L,R);
	else return Query(wh,L,n)+Query(wh,1,R);
}
int main()
{
	int op,x,y,v;
	Shai();
	for(int i=1;i<=150;++i)
	  {
	  	int t=i;
	  	for(int j=1;j<=en;++j)
	      while(t%prime[j]==0)
	        {
	          t/=prime[j];
	          ++zyz[i][j];
	        }
	  }
	scanf("%d",&zu);
	for(;zu;--zu)
	  {
	  	for(int i=1;i<=en;++i)
	  	  {
	  	  	T[i].clear();
	  	  	T2[i].clear();
	  	  }
	  	scanf("%d%d",&n,&m);
	  	for(;m;--m)
	  	  {
	  	  	scanf("%d%d%d%d",&op,&x,&y,&v);
	  	  	if(op==1)
	  	  	  {
	  	  	  	for(int i=1;i<=en;++i)
	  	  	  	  if(zyz[v][i])
	  	  	  	    {
	  	  	  	      if(x<=y)
	  	  	  	      	Add(i,x,y,zyz[v][i]);
	  	  	  	      else
	  	  	  	      	{
	  	  	  	      	  Add(i,x,n,zyz[v][i]);
	  	  	  	      	  Add(i,1,y,zyz[v][i]);
	  	  	  	      	}
	  	  	  	    }
	  	  	  }
	  	  	else if(op==2)
	  	  	  {
	  	  	  	for(int i=1;i<=en;++i)
	  	  	  	  if(zyz[v][i])
	  	  	  	    {
	  	  	  	      if(x<=y)
	  	  	  	      	Add(i,x,y,-zyz[v][i]);
	  	  	  	      else
	  	  	  	      	{
	  	  	  	      	  Add(i,x,n,-zyz[v][i]);
	  	  	  	      	  Add(i,1,y,-zyz[v][i]);
	  	  	  	      	}
	  	  	  	    }
	  	  	  }
	  	  	else
	  	  	  {
	  	  	  	ll ans=1;
	  	  	  	for(int i=1;i<=en;++i)
	  	  	  	  ans=(ans*Quick_Pow((ll)prime[i],QUERY(i,x,y),(ll)v))%(ll)v;
	  	  	  	printf("%I64d\n",ans);
	  	  	  }
	  	  }
	  }
	return 0;
}
时间: 2024-10-05 04:43:00

【分解质因数】【树状数组】【快速幂】codeforces 2014 ACM-ICPC Vietnam National Second Round E. ACM的相关文章

poj2182Lost Cows——树状数组快速查找

题目:http://poj.org/problem?id=2182 从后往前确定,自己位置之前没有被确定的且比自己编号小的个数+1即为自己的编号: 利用树状数组快速查找,可另外开一个b数组,角标为编号大小,而其值为是否使用,二分查找到恰好满足条件的位置,向后一直找到没被用过的第一个编号即为此位置编号. 代码如下: #include<iostream> #include<cstdio> using namespace std; int n,a[8005],f[8005],ans[80

Educational Codeforces Round 8(E. Zbazi in Zeydabad(树状数组优化))

题目链接:点击打开链接 题意:一个n*m矩阵, 里面的格子除了'z'就是'.',问有多少个z形图案. 思路:因为n和m很大, 即使n^3复杂度也会超时.  如果按照最朴素的方法, 我们可以处理一下前缀和, 处理出一个格子向左l[i][j].向右r[i][j].斜向左下lr[i][j]连着的z到哪里为止, 这样我们用n^2复杂度枚举每一个格子作为z形图案的右上角,取min(l[i][j], lr[i][j]), 就可以立刻知道这个z形的最左下角到哪里, 然后在这个对角线上扫一遍, 看看向右最远是不

HDU 1394 树状数组+离散化求逆序数

对于求逆序数问题,学会去利用树状数组进行转换求解方式,是很必要的. 一般来说我们求解逆序数,是在给定一串序列里,用循环的方式找到每一个数之前有多少个比它大的数,算法的时间复杂度为o(n2). 那么我们通过树状数组可以明显提高时间效率. 我们可以按照排列的顺序依次将数字放入树状数组中,并依次更新预与之相关联的树状数组元素.那么在将其更新完毕后,我们知道每个数对应的树状数组元素的左边的数肯定比它小,我们在以序列顺序依次更新树状数组时,如果有值在它前面出现,那么它对应的树状数组元素(在这个题目里存放的

11525 - Permutation(二分+树状数组)

题目链接:点击打开链接 题意:从1~k的所有排列中找到第n个排列, n由公式给出. 思路:可以发现, 这个公式就是康托展开公式(康托展开百科:点击打开链接). 那么s[i]的意思就是i个数中当前数排在第几. 如此, 可以用二分+树状数组快速求解, 和一道BC题目神似. 细节参见代码: #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<st

UVA 10869 - Brownie Points II(树状数组+离散化)

题目链接:点击打开链接 思路:统计区间和, 我们想到了树状数组, 离散化后, 枚举第一个人选取的x坐标, 用两个树状数组,以y坐标为下标建树, 一个表示当前左边的情况, 一个表示右边的情况, 再枚举当前垂直线上的每个点, 可以用树状数组快速统计结果, 该题题意挺难理解的, 要求输出第一个人的最小得分的最大值ans, 还有就是当第一个人取ans时第二个人的可能得分.时间复杂度O(nlogn) 细节参见代码: #include <cstdio> #include <cstring> #

1042.D Petya and Array 前缀 + 树状数组

11.19.2018 1042.D Petya and ArrayNew Point: 前缀 + 树状数组 :树状数组逐个维护前缀个数 Describe: 给你一个数组,一个标记数,问你有多少区间[l,r]使得这个区间的和小于这个标记数值 Solution: 没能想到 前缀数组 + 树状数组快速查询 记录前缀数组sum[i],得到区间和为sum[i] - sum[j] < t,转化为求sum[i] - t < sum[j],遍历i,求取情况,然后利用树状数组快速查询符合的区间j的个数 树状数组

Codeforces Round #424 (Div. 2) D 思维 E set应用,树状数组

Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) D. Office Keys 题意:一条直线上,有个办公室坐标 p,有 n个人在a[i],有 k把钥匙在b[i],每个人必须拿到一把钥匙,然后到办公室.问怎么安排花的时间最短. tags:还是不懂套路啊..其实多画两下图就能够感觉出来,2333 关键是要看出来,n个人拿的 n把钥匙应该是连续的. 然后,就是瞎暴力.. #include<bits/stdc++.h> usi

Codeforces Round #470 (Div 2) B 数学 C 二分+树状数组 D 字典树

Codeforces Round #470 B. Primal Sport 数学题,对 x2 和 x1 分解质因子即可. #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b;

CodeForces 380C Sereja and Brackets(扫描线+树状数组)

[题目链接] http://codeforces.com/problemset/problem/380/C [题目大意] 给出一个括号序列,求区间内左右括号匹配的个数. [题解] 我们发现对于每个右括号,其匹配的左括号是固定的, 我们保存每个右括号匹配的左括号位置, 对区间询问进行线扫描,将扫描的区间右端点及其之前所有的右括号对应的左括号位置做标记, 只要查询询问区间的标记个数就是答案,这个可以用树状数组维护. [代码] #include <cstdio> #include <algor