我只写了02 03 04,05 gcd 是我离现场切gcd最接近ac的一场,果然数论还是写不出gcd
1002 array
解法:如果这题不带修改操作,相信大家都会做,我就来讲讲怎么处理修改,对于每次修改操作,我不需要真的去修改它,而是把修改的那个元素插入set中去,然后每次查询,我们不带修改查一个答案v1,然后再去set里找第一个大于等于k的v2,min(v1, v2)就是答案。证明:如果v2 >= v1,不影响答案,如果v2<v1,,假设v2在数组中的位置 > r,那么显然很显然前 r 个数中一定不存在v2,那么不带修改查的v1一定小于等于v2,显然矛盾,也就是v2的位置一定<=r,证毕
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5 + 10, N = 1e5 + 1, inf = 1e9;
int rt[maxn], ls[maxn * 24], rs[maxn * 24], mn[maxn * 24], a[maxn], cnt, cat;
void build(int &o, int l, int r) {
o = ++cat;
if (l == r) {
mn[o] = l;
return;
}
int m =(l + r) / 2;
build(ls[o], l, m);
build(rs[o], m + 1, r);
mn[o] = min(mn[ls[o]], mn[rs[o]]);
}
void up(int &o, int pre, int l, int r, int k) {
o = ++cnt;
ls[o] = ls[pre];
rs[o] = rs[pre];
if (l == r) {
mn[o] = inf;
return;
}
int m = (l + r) / 2;
if (k <= m)
up(ls[o], ls[pre], l, m, k);
else
up(rs[o], rs[pre], m + 1, r, k);
mn[o] = min(mn[ls[o]], mn[rs[o]]);
}
int qu(int o, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr)
return mn[o];
int res = 1e9, m = (l + r) / 2;
if (ql <= m)
res = min(res, qu(ls[o], l, m, ql, qr));
if (qr > m)
res = min(res, qu(rs[o], m + 1, r, ql, qr));
return res;
}
set<int> s;
int main()
{
build(rt[0], 1, N);
int T;
scanf("%d", &T);
while (T--) {
cnt = cat;
s.clear();
int n, q, opt, r, k, ans = 0, x;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
up(rt[i], rt[i - 1], 1, N, a[i]);
}
while (q--) {
scanf("%d%d", &opt, &r);
r ^= ans;
if (opt == 1)
s.insert(a[r]);
else {
scanf("%d", &k);
k ^= ans;
int v = 1e9;
auto it = s.lower_bound(k);
if (it != s.end())
v = *it;
int v2 = qu(rt[r], 1, N, k, N);
ans = min(v, v2);
printf("%d\n", ans);
}
}
}
}
解法:我们先建后缀数组,根据sa数组建可持久化线段树,然后rmq预处理height数组,每次查询,我们根据height二分找到一个最左的合法的左端点LL,和最右的右端点RR,然后在可持久化线段树上找[LL, RR]的第 k 大的位置就行。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+1000, N = 1e5 + 10;
int rt[maxn], ls[maxn * 20], rs[maxn * 20], sum[maxn * 20], cnt;
void up(int &o, int pre, int l, int r, int k) {
o = ++cnt;
sum[o] = sum[pre] + 1;
ls[o] = ls[pre];
rs[o] = rs[pre];
if (l == r)
return;
int m = (l + r) / 2;
if (k <= m)
up(ls[o], ls[pre], l, m, k);
else
up(rs[o], rs[pre], m + 1, r, k);
}
int qu(int o, int pre, int l, int r, int k) {
if (l == r)
return l;
int cmp = sum[ls[o]] - sum[ls[pre]], m = (l + r) / 2;
if (cmp >= k)
return qu(ls[o], ls[pre], l, m, k);
return qu(rs[o], rs[pre], m + 1, r, k - cmp);
}
struct S_array
{
int s[maxn],sa[maxn],t[maxn],t2[maxn],c[maxn],n;
int mn[maxn][20];
void build_sa(int m)
{
int i,*x=t,*y=t2;
for(i=0;i<m;i++)c[i]=0;
for(i=0;i<n;i++)c[x[i]=s[i]]++;
for(i=1;i<m;i++)c[i]+=c[i-1];
for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
for(int k=1;k<=n;k<<=1){
int p=0;
for(i=n-k;i<n;i++)y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
for(i=0;i<m;i++)c[i]=0;
for(i=0;i<n;i++)c[x[y[i]]]++;
for(i=0;i<m;i++)c[i]+=c[i-1];
for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
if(p>=n)break;
m=p;
}
}
int rank[maxn],height[maxn];
void getHeight()
{
int i,j,k=0;
for(i=0;i<n;i++)rank[sa[i]]=i;
for(i=0;i<n;i++){
if(k)k--;
int j=sa[rank[i]-1];
while(s[i+k]==s[j+k])k++;
height[rank[i]]=k;
}
}
void init() {
cnt = 0;
for (int i = 1; i < n; i++)
up(rt[i], rt[i - 1], 0, N, sa[i]);
for (int i = 1; i < n; i++)
mn[i][0] = height[i];
for (int i = 1; i < 20; i++)
for (int j = 1; j - 1 + (1 << i) < n; j++)
mn[j][i] = min(mn[j][i - 1], mn[j + (1 << (i - 1))][i - 1]);
}
int rmq(int l, int r) {
int k = log2(r - l + 1);
return min(mn[l][k], mn[r + 1 - (1 << k)][k]);
}
int gao(int l, int r, int k) {
int pos = rank[l - 1];
int L = 1, R = pos, LL = pos, RR = pos;
while (L < R) {
int mid = (L + R) / 2;
if (rmq(mid, pos) >= r - l + 1)
R = mid;
else
L = mid + 1;
}
if (rmq(L, pos) >= r - l + 1)
LL = L - 1;
if (pos < n - 1) {
pos++;
L = pos, R = n - 1;
while (L < R) {
int mid = (L + R) / 2;
if (rmq(pos, mid) >= r - l + 1)
L = mid + 1;
else
R = mid;
}
if (rmq(pos, L) < r - l + 1)
L--;
if (L >= pos && rmq(pos, L) >= r - l + 1)
RR = L;
}
if (RR - LL + 1 < k)
return -1;
return qu(rt[RR], rt[LL - 1], 0, N, k) + 1;
}
} sa;
char s[maxn];
int main()
{
int T;
scanf("%d", &T);
while (T--) {
int n, q, l, r, k;
scanf("%d%d%s", &n, &q, s + 1);
for (int i = 1; i <= n; i++)
sa.s[i - 1] = s[i] - 'a' + 1;
sa.s[n] = 0;
sa.n = n + 1;
sa.build_sa(27);
sa.getHeight();
sa.init();
while (q--) {
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", sa.gao(l, r, k));
}
}
}
解法:我们对于所有点u,对它的出边按照边权排序,然后用一个set维护当前优先队列存了的所有路径长度,但是只维护max(k)个元素,我们把所有边存进优先队列,每次出队,我们枚举当前节点的所有出边,如果set元素数量低于max(k)或者当前路径长度+当前出边小于set最大的元素,那么我们就把新的路径长度更新到set并且存进优先队列,否则中断枚举,直到优先队列出队max(k)次即可
#include<bits/stdc++.h>
#define pi pair<int, int>
#define mk make_pair
#define ll long long
using namespace std;
const int maxn = 5e4 + 10;
struct node {
int u;
ll dis;
bool operator<(const node& t) const {
return dis > t.dis;
}
};
vector<pi> G[maxn];
priority_queue<node> q;
multiset<ll> s;
ll ans[maxn * 10];
int qry[maxn];
void init(int n) {
s.clear();
for (int i = 1; i <= n; i++)
G[i].clear();
while (!q.empty())
q.pop();
}
int main()
{
int T;
scanf("%d", &T);
while (T--) {
int n, m, Q, u, v, w, k, mx = 0;
scanf("%d%d%d", &n, &m, &Q);
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &u, &v, &w);
G[u].push_back(mk(w, v));
q.push(node{v, w});
s.insert(w);
}
for (int i = 1; i <= n; i++)
sort(G[i].begin(), G[i].end());
for (int i = 1; i <= Q; i++)
scanf("%d", &qry[i]), mx = max(mx, qry[i]);
int cnt = 0;
while (cnt < mx) {
node tmp = q.top();
q.pop();
ans[++cnt] = tmp.dis;
if (cnt >= mx)
break;
u = tmp.u;
for (auto it : G[u]) {
v = it.second;
ll w = it.first + tmp.dis;
if (s.size() == mx) {
auto cat = --s.end();
if (w >= *cat)
break;
s.erase(cat);
s.insert(w);
}
else
s.insert(w);
q.push(node{v, w});
}
}
for (int i = 1; i <= Q; i++)
printf("%lld\n", ans[qry[i]]);
init(n);
}
}
1005 huntian oy
其实问题可以转化成:
∑
i
=
1
n
∑
j
=
1
i
(
i
−
j
)
[
g
c
d
(
i
,
j
)
=
1
]
\sum_{i = 1}^{n}\sum_{j = 1}^{i}(i-j)[gcd(i,j)=1]
∑i=1n∑j=1i(i−j)[gcd(i,j)=1]
进一步转化:
∑
i
=
1
n
i
φ
(
i
)
−
∑
i
=
1
n
∑
j
=
1
i
j
[
g
c
d
(
i
,
j
)
=
1
]
\sum_{i =1}^{n}i\varphi (i)-\sum_{i=1}^{n}\sum_{j=1}^{i}j[gcd(i,j)=1]
∑i=1niφ(i)−∑i=1n∑j=1ij[gcd(i,j)=1]
对于所有
i
(
i
>
=
2
)
i(i>=2)
i(i>=2),假设
j
j
j与
i
i
i互质,那么
i
−
j
i-j
i−j一定与
i
i
i互质,也就是说所有小与
i
i
i且
g
c
d
(
i
,
j
)
=
1
gcd(i,j)=1
gcd(i,j)=1的
j
j
j的总和,一定等于
i
φ
(
i
)
2
\frac{i\varphi (i)}{2}
2iφ(i),那么上式变形:
a
n
s
=
1
2
∑
i
=
2
n
i
φ
(
i
)
ans=\frac{1}{2}\sum_{i=2}^{n}i\varphi (i)
ans=21∑i=2niφ(i)
这就是个简单杜教筛,于是搞定这题啦
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 5e6 + 10, N = 5e6, mod = 1e9 + 7;
int pri[maxn], vis[maxn], phi[maxn], cnt, inv, inv2;
unordered_map<int, int> mp;
void add(int &x, int y) {
x += y;
if (x >= mod)
x -= mod;
if (x < 0)
x += mod;
}
ll ksm(ll x, int y) {
ll res = 1;
while (y) {
if (y & 1)
res = res * x % mod;
x = x * x % mod;
y /= 2;
}
return res;
}
void init(int n) {
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i])
pri[++cnt] = i, phi[i] = i - 1;
for (int j = 1; j <= cnt && pri[j] * i <= n; j++) {
vis[pri[j] * i] = 1;
if (i % pri[j])
phi[i * pri[j]] = phi[i] * phi[pri[j]];
else {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
for (int i = 1; i <= n; i++) {
phi[i] = phi[i - 1] + 1ll * phi[i] * i % mod;
add(phi[i], 0);
}
}
int calc(int n) {
return 1ll * n * (n + 1) / 2 % mod;
}
int calc1(int n) {
return 1ll * n * (n + 1) % mod * (2 * n + 1) % mod * inv2 % mod;
}
int dfs1(int n) {
if (n <= N)
return phi[n];
if (mp.count(n))
return mp[n];
int ans = calc1(n);
for (int l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
ans -= 1ll * (calc(r) - calc(l - 1) + mod) * dfs1(n / l) % mod;
add(ans, 0);
}
mp.emplace(n, ans);
return ans;
}
int main()
{
inv = ksm(2, mod - 2);
inv2 = ksm(6, mod - 2);
init(N);
int T;
scanf("%d", &T);
while (T--) {
int n, a, b;
scanf("%d%d%d", &n, &a, &b);
int ans = dfs1(n) - 1;
ans = 1ll * ans * inv % mod;
printf("%d\n", ans );
}
}