[清华集训2014]奇数国

OJ题号:
UOJ38、BZOJ3813

题目大意:
一个长度为$1000000$的数列,提供以下两种操作:
1.修改某一点的数;
2.求某一区间乘积的欧拉函数。
保证每个元素的最大质因数不超过$281$,答案对$19961933$取模。

思路:
$\varphi(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_l})$,其中$p$为$n$的不同的质因数。
可以发现欧拉函数值只与$n$和$p$有关。
所以可以用线段树维护每个区间的乘积,因为$281$是第$60$个质数,因此可以再用一个unsigned long long压位存储每个质数是否出现。
最后套用欧拉函数公式,除法运算相当于乘以逆元。

实现细节:
注意位运算中的1<<i默认是int类型,要转为1ull<<i,否则会WA5个点。

 1 #include<map>
 2 #include<cmath>
 3 #include<cstdio>
 4 #include<cctype>
 5 #include<vector>
 6 inline int getint() {
 7     char ch;
 8     while(!isdigit(ch=getchar()));
 9     int x=ch^‘0‘;
10     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^‘0‘);
11     return x;
12 }
13 const int N=1000001,n=1000000,mod=19961993;
14 std::map<int,int> id;
15 inline bool isPrime(const int x) {
16     for(int i=2;i<=floor(sqrt(x));i++) if(!(x%i)) return false;
17     return true;
18 }
19 std::vector<int> v;
20 int inv[282];
21 class SegmentTree {
22     #define mid ((b+e)>>1)
23     #define _left <<1
24     #define _right <<1|1
25     private:
26         int val[N<<2];
27         unsigned long long prime[N<<2];
28         void push_up(const int p) {
29             val[p]=(long long)val[p _left]*val[p _right]%mod;
30             prime[p]=prime[p _left]|prime[p _right];
31         }
32         std::pair<int,unsigned long long> query(const int p,const int b,const int e,const int l,const int r) {
33             if((b==l)&&(e==r)) return std::make_pair(val[p],prime[p]);
34             std::pair<int,unsigned long long> q1=std::make_pair(1,0),q2=std::make_pair(1,0),ret;
35             if(l<=mid) q1=query(p _left,b,mid,l,std::min(mid,r));
36             if(r>mid) q2=query(p _right,mid+1,e,std::max(mid+1,l),r);
37             ret.first=(long long)q1.first*q2.first%mod;
38             ret.second=q1.second|q2.second;
39             return ret;
40         }
41     public:
42         void build(const int p,const int b,const int e) {
43             if(b==e) {
44                 val[p]=3;
45                 prime[p]=2;
46                 return;
47             }
48             build(p _left,b,mid);
49             build(p _right,mid+1,e);
50             push_up(p);
51         }
52         void modify(const int p,const int b,const int e,const int x,const int y) {
53             if(b==e) {
54                 val[p]=y;
55                 prime[p]=0;
56                 for(unsigned i=0;i<v.size();i++) {
57                     if(!(y%v[i])) prime[p]|=1ull<<i;
58                 }
59                 return;
60             }
61             if(x<=mid) modify(p _left,b,mid,x,y);
62             if(x>mid) modify(p _right,mid+1,e,x,y);
63             push_up(p);
64         }
65         int phi(const int p,const int b,const int e,const int l,const int r) {
66             std::pair<int,unsigned long long> tmp=query(1,1,n,l,r);
67             int ans=tmp.first;
68             for(unsigned i=0;i<60;i++) {
69                 if(tmp.second&(1ull<<i)) {
70                     ans=(long long)ans*(v[i]-1)%mod*inv[v[i]]%mod;
71                 }
72             }
73             return ans;
74         }
75 };
76 SegmentTree t;
77 int main() {
78     inv[1]=1;
79     for(int i=2,cnt=0;i<=281;i++) {
80         if(isPrime(i)) {
81             id[i]=cnt++;
82             v.push_back(i);
83         }
84         inv[i]=(long long)(mod-mod/i)*inv[mod%i]%mod;
85     }
86     int x=getint();
87     t.build(1,1,n);
88     for(int i=1;i<=x;i++) {
89         int a=getint(),b=getint(),c=getint();
90         if(a) {
91             t.modify(1,1,n,b,c);
92         }
93         else {
94             printf("%d\n",t.phi(1,1,n,b,c));
95         }
96     }
97     return 0;
98 }

事实上质数只有60个,所以可以直接打表。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<algorithm>
 4 inline int getint() {
 5     char ch;
 6     while(!isdigit(ch=getchar()));
 7     int x=ch^‘0‘;
 8     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^‘0‘);
 9     return x;
10 }
11 const int N=1000001,n=1000000,mod=19961993;
12 const int v[60]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281};
13 const int inv[60]={9980997,6653998,11977196,8555140,5444180,1535538,10568114,14708837,3471651,11701858,17386252,1618540,16066970,2321162,18263100,16948862,12518538,15380552,10725847,1686929,13399146,17182475,12025297,15924736,13582387,395287,6395590,15857658,16299242,6359573,3300802,18742940,6702567,10914471,16210746,11765678,5340151,18247466,7769638,8077107,11932588,6506948,1985748,6619521,5877135,4413707,9744480,10115270,14597757,16475182,18334191,5011379,18885205,7555336,621385,11309266,12170137,12006660,18304499,11153142};
14 class SegmentTree {
15     #define mid ((b+e)>>1)
16     #define _left <<1
17     #define _right <<1|1
18     private:
19         int val[N<<2];
20         unsigned long long prime[N<<2];
21         void push_up(const int p) {
22             val[p]=(long long)val[p _left]*val[p _right]%mod;
23             prime[p]=prime[p _left]|prime[p _right];
24         }
25         std::pair<int,unsigned long long> query(const int p,const int b,const int e,const int l,const int r) {
26             if((b==l)&&(e==r)) return std::make_pair(val[p],prime[p]);
27             std::pair<int,unsigned long long> q1=std::make_pair(1,0),q2=std::make_pair(1,0),ret;
28             if(l<=mid) q1=query(p _left,b,mid,l,std::min(mid,r));
29             if(r>mid) q2=query(p _right,mid+1,e,std::max(mid+1,l),r);
30             ret.first=(long long)q1.first*q2.first%mod;
31             ret.second=q1.second|q2.second;
32             return ret;
33         }
34     public:
35         void build(const int p,const int b,const int e) {
36             if(b==e) {
37                 val[p]=3;
38                 prime[p]=2;
39                 return;
40             }
41             build(p _left,b,mid);
42             build(p _right,mid+1,e);
43             push_up(p);
44         }
45         void modify(const int p,const int b,const int e,const int x,const int y) {
46             if(b==e) {
47                 val[p]=y;
48                 prime[p]=0;
49                 for(unsigned i=0;i<60;i++) {
50                     if(!(y%v[i])) prime[p]|=1ull<<i;
51                 }
52                 return;
53             }
54             if(x<=mid) modify(p _left,b,mid,x,y);
55             if(x>mid) modify(p _right,mid+1,e,x,y);
56             push_up(p);
57         }
58         int phi(const int p,const int b,const int e,const int l,const int r) {
59             std::pair<int,unsigned long long> tmp=query(1,1,n,l,r);
60             int ans=tmp.first;
61             for(unsigned i=0;i<60;i++) {
62                 if(tmp.second&(1ull<<i)) {
63                     ans=(long long)ans*(v[i]-1)%mod*inv[i]%mod;
64                 }
65             }
66             return ans;
67         }
68 };
69 SegmentTree t;
70 int main() {
71     t.build(1,1,n);
72     for(int x=getint();x;x--) {
73         int a=getint(),b=getint(),c=getint();
74         if(a) {
75             t.modify(1,1,n,b,c);
76         }
77         else {
78             printf("%d\n",t.phi(1,1,n,b,c));
79         }
80     }
81     return 0;
82 }

时间: 2024-10-22 03:59:45

[清华集训2014]奇数国的相关文章

AC日记——【清华集训2014】奇数国 uoj 38

#38. [清华集训2014]奇数国 思路: 题目中的number与product不想冲: 即为number与product互素: 所以,求phi(product)即可: 除一个数等同于在模的意义下乘以一个数的逆元: 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 100005

清华集训2014 做题记录

清华集训2014做题记录 已完成 [清华集训2014]玛里苟斯 [清华集训2014]主旋律 [清华集训2014]奇数国 [清华集训2014]矩阵变换 [清华集训2014]sum [清华集训2014]虫逢 [清华集训2014]玄学 [清华集训2014]文学 未完成 [清华集训2014]卡常数 [清华集训2014]简单回路 [清华集训2014]Router [清华集训2014] Breaking Bomber 写一题要膜一题题解,膜完题解膜代码,膜完代码膜指导,膜了好几天了还有四个题没做. [清华集

「清华集训2014」主旋律

「清华集训2014」主旋律 这个题好难难啊,我想了一个小时连50分都不会,只能去摸周指导了. 我们试图直接爆算集合 \(S\) 的非强连通导出子图数量,考虑将这个导出子图的所有强连通分量缩点后,一定是一个点数 \(\geq 2\) 的 \(\text{DAG}\) .即缩完以后至少要有一个入度为 \(0\) 的点,这个条件充分性显然,必要性考虑如果不存在入度为 \(0\) 的点,那么一定存在一个环,说明并没有将所有强连通分量缩掉.然后我们枚举入度为 \(0\) 的点集 \(T\) ,记 \(F(

uoj 41 【清华集训2014】矩阵变换 婚姻稳定问题

[清华集训2014]矩阵变换 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://uoj.ac/problem/41 Description 给出一个 N 行 M 列的矩阵A, 保证满足以下性质: M>N.    矩阵中每个数都是 [0,N] 中的自然数.    每行中, [1,N] 中每个自然数都恰好出现一次.这意味着每行中 0 恰好出现 M−N 次.    每列中,[1,N] 中每个自然数至多出现一次. 现在我们要在每行中选取一个非零数,

「清华集训2014」矩阵变换

「清华集训2014」矩阵变换 解题思路 问题转化为找一个行与选的数字的完美匹配,记 \(pos[i][j]\) 为数字 \(i\) 在第 \(j\) 行的出现位置,要求不存在匹配边 \((a,b),(c,d)\) 使得 \(pos[b][a] < pos[b][c]\ \& \ pos[d][c] > pos[b][c]\) . 观察发现,这个式子相当于让一条边的权值看做 \(pos[x][y]\) ,当存在一个行与一个数没有匹配但是强行让它们匹配后数的权值会变大,行的权值会变小,这个

UOJ#46. 【清华集训2014】玄学

传送门 分析 清华集训真的不是人做的啊嘤嘤嘤 我们可以考虑按操作时间把每个操作存进线段树里 如果现在点x正好使一个整块区间的右端点则更新代表这个区间的点 我们不难发现一个区间会因为不同的操作被分成若干块,每块对应序列上不同的区间 于是查询时对于每个线段树上区间查询时二分查找当前点在哪一块中即可 代码 #include<iostream> #include<cstdio> #include<cstring> #include<string> #include&

【UOJ#38】【清华集训2014】奇数国

考虑欧拉函数的性质,60很小,压位存下线段树每个节点出现质数. #include<bits/stdc++.h> const int N=100010; const int yql=19961993; typedef long long ll; using namespace std; int prime[100],n,m,vis[910]; ll val,p,rev[100]; struct Segment_Tree{ #define lson (o<<1) #define rso

[UOJ]#36. 【清华集训2014】玛里苟斯

题目大意:给n个数字,求子集的异或和的k次方的期望(n<=10^5,k<=5,保证答案小于2^63) 做法:首先如果从集合中拿出a和b,把a和a xor b放回集合,子集的异或和与原来是一一对应的,用高斯消元的思想可以消到只剩log个数,其他都是0,对答案没有影响.然后考虑k次方的期望,我们把二进制下每一位拆开,假设第i位的数字为xi,答案为(x1+x2+...+xlog)^k的期望,展开式子后发现是选k次x1~xlog中的数(可以重复选),每种选法选的位的乘积的期望的和,暴力枚举每种选法,复

【uoj#37/bzoj3812】[清华集训2014]主旋律 状压dp+容斥原理

题目描述 求一张有向图的强连通生成子图的数目对 $10^9+7$ 取模的结果. 题解 状压dp+容斥原理 设 $f[i]$ 表示点集 $i$ 强连通生成子图的数目,容易想到使用总方案数 $2^{sum[i]}$ 减去不为强连通图的方案数得到强连通图的方案数,其中 $sum[i]$ 表示点集 $i$ 中边的数目. 考虑什么样的图不是强连通图:缩点后入度为0的强连通分量对应的点集不是全集. 枚举这些入度为0的强连通分量对应的点集,由于无法保证只有这些点构成的入度为0的强连通分量,因此需要进一步容斥.