感谢刘老师提供了Bestcoder比赛平台,感谢Herobs在技术上的各种帮助。
[title]1001 Sum Sum Sum[/title]
直接暴力判断素数即可,特殊情况1也是P-number。
[title]1002 Sit sit sit[/title]
设$dp[0][i][j]$表示前i个位置已经确定了被坐的相对次序的合法情况数,并且第i个位置为当中第j个被坐的,第i个位置在第i-1个位置之后被坐。
同理$dp[1][i][j]$表示前i个位置已经确定了被坐的相对次序的合法情况数,并且第i个位置为当中第j个被坐的,第i个位置在第i-1个位置之前被坐。
状态转移方程(当i-1和i+1座位颜色不同时):
$dp[0][i][j]=\sum_{k=1}^{j-1}(dp[0][i-1][k]+dp[1][i-1][k])$
$dp[1][i][j]=\sum_{k=j}^{i-1}dp[1][i-1][k]$
中间边求边处理前缀和即可。
时间复杂度:可以O(n^3)过,也可以处理前缀和优化成O(n^2)。
[title]1003 A Strange Problem[/title]
对于操作二,利用公式
当x >= Phi(C), A^x = A ^ (x%Phi(C) + Phi(C)) (mod C)
对于2333333这个模数来说,求18次欧拉函数后就变成了1,所以只需要保存19层第三次操作的加数即可,然后就直接是线段树区间更新和询问操作了。
Ps:很多人把题目想简单了,操作二不能直接简单的更新,不满足2^(2^x) mod P = 2^( 2^x mod P) mod P。
```cpp
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <ctime>
#include <iostream>
using namespace std;
typedef __int64 ll;
const int MOD = 2333333;
const int N = 50000+5;
int mo[20] = {2333333,2196720,580608,165888,55296,18432,
6144,2048,1024,512,256,128,64,32,16,8,4,2,1};
int pow2[33];
vector<ll> vt[N];
struct Segtree {
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
ll mark[N<<2];
int length[N<<2], sum[N<<2];
void build(int rt, int l, int r) {
mark[rt] = 0;
length[rt] = r-l+1;
if(l == r) {
vt[l].clear();
int x;
scanf("%d", &x);
vt[l].push_back(x);
sum[rt] = x%MOD;
return ;
}
int mid = l+r>>1;
build(lson); build(rson);
up(rt);
}
inline void Add(int &x, int y) {
x += y;
if(x >= MOD) x -= MOD;
}
void up(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
Add(sum[rt], 0);
}
void down(int rt) {
if(mark[rt]) {
mark[rt<<1] += mark[rt];
mark[rt<<1|1] += mark[rt];
Add(sum[rt<<1], 1LL*length[rt<<1]*mark[rt]%MOD);
Add(sum[rt<<1|1], 1LL*length[rt<<1|1]*mark[rt]%MOD);
mark[rt] = 0;
}
}
void update(int rt, int l, int r, int L, int R, int v) {
if(L <= l && R >= r) {
mark[rt] += v;
Add(sum[rt], 1LL*length[rt]*v%MOD);
return ;
}
down(rt);
int mid = l+r>>1;
if(L <= mid) update(lson, L, R, v);
if(R > mid) update(rson, L, R, v);
up(rt);
}
int pow_mod(int x, int n, int mod) {
int ret = 1%mod;
while(n) {
if(n&1) ret = 1LL*ret*x%mod;
x = 1LL*x*x%mod;
n >>= 1;
}
return ret;
}
int cal(vector<ll> &v) {
if(v.size() < 19) {
int pos = v.size()-1;
ll ret = v[0];
bool flag = false;
if(v[0] >= mo[pos]) {
flag = true;
ret = ret%mo[pos] + mo[pos];
}
pos--;
for(int i = 1;i < v.size(); i++) {
if(flag) {
ret = (pow_mod(2, ret, mo[pos]) +v[i])%mo[pos] + mo[pos];
}
else {
if(ret >= 30) {
flag = true;
ret = (pow_mod(2, ret, mo[pos]) + v[i])%mo[pos]+mo[pos];
}
else if(pow2[ret] >= mo[pos]) {
flag = true;
ret = (pow_mod(2, ret, mo[pos]) + v[i])%mo[pos] + mo[pos];
}
else {
ret = pow2[ret] + v[i];
if(ret >= mo[pos]) {
flag = true;
ret = ret%mo[pos] + mo[pos];
}
}
}
pos--;
}
return ret%MOD;
}
else {
ll ret = 1;
int pos = 17;
bool flag = true;
for(int i = v.size()-18;i < v.size(); i++) {
ret = (pow_mod(2, ret, mo[pos]) + v[i])%mo[pos] + mo[pos];
pos--;
}
return ret%MOD;
}
}
void modify(int rt, int l, int r, int x) {
if(l == r) {
if(mark[rt]) {
vt[l][vt[l].size()-1] += mark[rt];
mark[rt] = 0;
}
vt[l].push_back(0);
sum[rt] = cal(vt[l]);
return ;
}
down(rt);
int mid = l+r>>1;
if(x <= mid) modify(lson, x);
else modify(rson, x);
up(rt);
}
int query(int rt, int l, int r, int L, int R) {
if(L <= l && R >= r) return sum[rt];
down(rt);
int mid = l+r>>1, ret = 0;
if(L <= mid) Add(ret, query(lson, L, R));
if(R > mid) Add(ret, query(rson, L, R));
up(rt);
return ret;
}
}tree;
int n, m;
void init() {
for(int i = 0;i <= 30; i++) pow2[i] = 1<<i;
}
int main() {
init();
int op, l, r, x;
while(scanf("%d%d", &n, &m) == 2) {
tree.build(1, 1, n);
while(m--) {
scanf("%d", &op);
if(op == 1) {
scanf("%d%d", &l, &r);
printf("%d\n", tree.query(1, 1, n, l, r));
}
else if(op == 2) {
scanf("%d", &x);
tree.modify(1, 1, n, x);
}
else {
scanf("%d%d%d", &l, &r, &x);
tree.update(1, 1, n, l, r, x);
}
}
}
return 0;
}
```
[title]1004 A card problem[/title]
一个莫比乌斯反演的题目。
设$F(d)$为$gcd(B_{i},B_{j})$为d的倍数的pair对,f(d)为$gcd(B_{i},B_{j})=d$的pair对,则$f(d)=\sum_{d|n}F(n)\cdot mu(n/d)$。设$P_{i}$为第i个质数,所以我们要求的就是
$f(1)+f(P_{1})+f(P_{1}^{2})+...+f(P_{2})+f(P_{2}^2)+...$,化简后得
$\sum_{i=1}^{N}F(i)\cdot(mu(1)+\sum_{P_{j}|i}mu(i/P_{j})+\sum_{P_{j}^{2}|i}mu(i/P_{j}^{2})+...)$
然后就是要计算所有的$F(d)$,对于计算$F(d)$,(i,j)的good pairs有
$F(d)=\frac{A_{i}}{d}\cdot \frac{A_{j}}{d}\cdot \frac{\prod_{k=1}^{N}A_{k}}{A_{i}\cdot A_{j}}(1\leq i< j\leq N)$,然后对于i可以处理出$G(i)=\frac{1}{A_{i}}$,然后对于1~N的G(i)加起来平方下,得到的即为两两间的乘积,然后把自己和自己乘积的减掉再除2可以了,中间的除法运算用逆元,最后是分段来处理即可,时间复杂度为O(nlogn)。计算出F(d)的话也可以直接容斥搞。
```cpp
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <iostream>
using namespace std;
const int MOD = (int)1e9+7;
const int N = 100000+5;
int pri[N], pnum, mu[N], cnt[N], F[N], inv2, a[N], su[N], sum[N], p[N], Inv[N];
bool vis[N];
void mobius(int n) {
mu[1] = 1;
vis[1] = 1;
for(int i = 2;i <= n; i++) {
if(!vis[i]) pri[pnum++] = i, mu[i] = -1;
for(int j = 0;j < pnum; j++) {
if(i*pri[j] > n) break;
vis[i*pri[j]] = 1;
if(i%pri[j] == 0) {
mu[i*pri[j]] = 0;
break;
}
mu[i*pri[j]] = -mu[i];
}
}
for(int i = 1;i <= n; i++) {
if(!vis[i]) p[i] = i;
if(!p[i]) continue;
if(n/i >= p[i]) p[i*p[i]] = p[i];
for(int j = i;j <= n;j += i)
cnt[j] += mu[j/i];
}
for(int i = 1;i <= n; i++) cnt[i] += mu[i];
}
int pow_mod(int x, int n) {
int ret = 1;
while(n) {
if(n&1) ret = 1LL*ret*x%MOD;
x = 1LL*x*x%MOD;
n >>= 1;
}
return ret;
}
void init(int n) {
for(int i = 1;i <= n; i++) Inv[i] = pow_mod(i, MOD-2);
inv2 = pow_mod(2, MOD-2);
}
inline void Add(int &x, int y) {
x += y;
if(x >= MOD) x -= MOD;
if(x < 0) x += MOD;
}
int main() {
mobius(100000);
init(100000);
int n;
while(scanf("%d", &n) == 1) {
int mx = 0;
for(int i = 1;i <= n; i++) scanf("%d", &a[i]), mx = max(mx, a[i]);
for(int i = 1;i <= mx; i++) {
su[i] = sum[i] = 0;
}
int fk = 1;
for(int i = 1;i <= n; i++) fk = 1LL*fk*a[i]%MOD;
for(int i = 1;i <= n; i++) {
int cur = Inv[a[i]];
Add(su[a[i]], 1LL*cur*cur%MOD);
Add(sum[1], cur); Add(sum[a[i]+1], -cur);
}
for(int i = 1;i <= mx; i++) Add(su[i], su[i-1]);
for(int i = 1;i <= mx; i++) Add(sum[i], sum[i-1]);
for(int i = 1;i <= mx; i++) {
int cur = 0, cur2 = 0;
for(int j = i;j <= mx; j += i) {
Add(cur, sum[j]);
int nxt = min(mx, j+i-1);
int tmp = 1LL*(j/i)*(j/i)%MOD;
Add(cur2, 1LL*tmp*(su[nxt]-su[j-1])%MOD);
}
cur = 1LL*cur*cur%MOD;
F[i] = 1LL*(cur - cur2)*inv2%MOD;
if(F[i] < 0) F[i] += MOD;
}
int ans = 0;
for(int i = 1;i <= mx; i++) {
Add(ans, 1LL*F[i]*cnt[i]%MOD);
}
printf("%d\n", 1LL*fk*ans%MOD);
}
return 0;
}
```