Content
[CF474F]Ant colony
\(n\) 个数 \(a_{1\sim n}\),\(m\) 次询问,每次给出 \([l,r]\),求所有不满足 \(\forall j\in [l,r],a_i|a_j\) 的 \(i\) 的数量。
\(1\le n,m\le 10^5,1\le a_i\le 10^9\)。
考虑用区间长度减去满足条件的 \(i\) 的数量。
显然,如果 \(\gcd(a_{l},a_{l+1},\ldots,a_{r})=a_i\),那么 \(i\) 满足条件。
那么 ST 表+扫描线维护一下即可。
时间复杂度 \(O(n\log n\log a)\)。
Code
#include <bits/stdc++.h>
const int maxn = 1e5 + 5;
int gcd(int x,int y) {
return y ? gcd(y , x % y) : x;
}
int n,m,a[maxn],cnt,f[maxn][20],lg[maxn],ans[maxn];
struct scanline {
int id,v,pos,x;
scanline() {
id = v = pos = x = 0;
}
scanline(int id,int v,int pos,int x):id(id),v(v),pos(pos),x(x){}
}s[maxn << 1];
std::map<int,int> sum;
int GCD(int l,int r) {
int k = lg[r - l + 1];
return gcd(f[l][k] , f[r - (1 << k) + 1][k]);
}
int main() {
scanf("%d",&n);
for(int i = 1;i <= n;++ i)scanf("%d",&a[i]),f[i][0] = a[i];
for(int i = 2;i <= n;++ i)lg[i] = lg[i >> 1] + 1;
for(int k = 1;(1 << k) <= n;++ k) {
for(int i = 1;i + (1 << k) - 1 <= n;++ i) {
f[i][k] = gcd(f[i][k - 1] , f[i + (1 << k - 1)][k - 1]);
}
}
scanf("%d",&m);
for(int i = 1;i <= m;++ i) {
int l,r;
scanf("%d %d",&l,&r);
int t = GCD(l , r);
s[++ cnt] = scanline(i , 1 , l - 1 , t);
s[++ cnt] = scanline(i , -1 , r , t);
ans[i] = r - l + 1;
}
std::sort(s + 1 , s + 1 + cnt , [&](const scanline& p,const scanline& q) {
return p.pos < q.pos;
});
for(int i = 1,j = 1;i <= cnt;++ i) {
for(;j <= s[i].pos;++ j) {
++ sum[a[j]];
}
if(sum.find(s[i].x) != sum.end())ans[s[i].id] += s[i].v * sum[s[i].x];
}
for(int i = 1;i <= m;++ i)printf("%d\n",ans[i]);
return 0;
}
[CF505C]Mr. Kitayuta, the Treasure Hunter
一条数轴,有 \(n\) 个特殊点,一个人从 \(0\) 开始走,第一次可以沿正方向走 \(d\) 格,接着他可以走 \(c-1\) 或 \(c\) 或 \(c+1\) 步(其中 \(c\) 为上一次走的步数)。
求经过的最多特殊点数。
\(1\le n,d\le 3\times 10^4\),特殊点在 \([1,3\times 10^4]\) 范围内。
显然有一个基本的 DP 思路:令 \(f(i,j)\) 表示走到第 \(i\) 格,最后一步走了 \(j\) 步的最大特殊点数。
状态转移方程:\(f(i,j)=\max\{f(i-j,j),f(i-j,j-1),f(i-j,j+1)\}+sum_i\),其中 \(sum_i\) 表示第 \(i\) 格的特殊点数。
因为 \(n\times d\) 很大,显然过不了。
其实我们发现,\(d\) 虽然很大,但它的变化量不会太大,是 \(O(\sqrt{n})\) 级别的。
这里我懒得算了,看了眼题解,变化量取到 \(320\) 就完全没问题。
那么用变化量代替步长放入状态即可。\(f(i,j)\) 表示第 \(i\) 格,最后一步 \(d+j\) 格的最大答案。
显然 \(f(i,j)=\max\{f(i-(d+j),j-1),f(i-(d+j),j),f(i-(d+j),j+1)\}+sum_i\)。
边界问题特判即可。至于变化量负数的问题,直接在数组里多加 \(320\) 即可。
时间复杂度为 \(O(n\sqrt{n})\) 级别。
Code
超级短的代码。
#include <bits/stdc++.h>
const int maxn = 3e4 + 5;
const int maxm = 650;
const int SIZE = 320;
int n,d,a[maxn],sum[maxn],f[maxn][maxm];
int main() {
scanf("%d %d",&n,&d);
int val = 0;
for(int i = 1;i <= n;++ i) {
scanf("%d",&a[i]);
++ sum[a[i]];
}
memset(f , 0xcf , sizeof(f));
int ans;
f[d][SIZE] = ans = sum[0] + sum[d];
for(int i = d + 1;i <= a[n];++ i) {
for(int k = -320;k <= 320;++ k) {
if(d + k <= 0)continue ;
if(d + k > i)break ;
ans = std::max(ans , f[i][k + SIZE] = std::max(f[i - (d + k)][k + SIZE - 1] , std::max(f[i - (d + k)][k + SIZE] , f[i - (d + k)][k + SIZE + 1])) + sum[i]);
}
}
printf("%d",ans);
return 0;
}
[CF310D]Yaroslav and Divisors
给定一个 \(1\sim n\) 的排列 \(p\),\(m\) 次询问,每次询问 \([l,r]\) 中有多少对 \((i,j)(i\le j)\) 满足 \(p_i|p_j\) 或 \(p_j|p_i\)。
\(1\le n,m\le 2\times 10^5\)。
看完题,发现题中所给的这个序列是 \(1\sim n\) 的排列,这个条件非常奇怪。
它其实是在说,\([1,n]\) 的 \((i,j)\) 个数为 \(O(n\log n)\)。
并且,这类区间内点对统计的离线做法一般都是按 \(r\) 排序。
考虑这种处理手法,拍完序后,直接暴力树状数组修改即可。
预处理有一些细节:设 \(pos_x=i(p_i=x)\)。
当枚举到 \((i,j)(i|j)\) 时,把 \(pos\) 值较小者插入较大者的 std::vector
中。
原因不难理解。可以参考洛谷题解。
Code
#include <bits/stdc++.h>
#define pb emplace_back
const int maxn = 2e5 + 5;
std::vector<int> G[maxn];
int n,m,a[maxn],ans[maxn],pos[maxn];
struct node {
int id,l,r;
node() {
id = l = r = 0;
}
}Q[maxn];
int c[maxn];
int lowbit(int x) {
return x & -x;
}
void add(int x,int y) {
for(;x <= n;x += lowbit(x))c[x] += y;
return ;
}
int query(int x) {
int ans = 0;
for(;x;x -= lowbit(x))ans += c[x];
return ans;
}
int main() {
scanf("%d %d",&n,&m);
for(int i = 1;i <= n;++ i)scanf("%d",&a[i]),pos[a[i]] = i;
for(int i = 1;i <= n;++ i) {
for(int j = i << 1;j <= n;j += i) {
if(pos[j] > pos[i])G[j].pb(i);
else G[i].pb(j);
}
}
for(int i = 1;i <= m;++ i) {
Q[i].id = i;
scanf("%d %d",&Q[i].l,&Q[i].r);
}
std::sort(Q + 1 , Q + 1 + m , [&](const node& p,const node& q) {
return p.r < q.r;
});
for(int i = 1,j = 1;i <= m;++ i) {
for(;j <= Q[i].r;++ j) {
for(auto& v : G[a[j]])add(pos[v] , 1);
}
ans[Q[i].id] = Q[i].r - Q[i].l + 1 + query(Q[i].r) - query(Q[i].l - 1);
}
for(int i = 1;i <= m;++ i)printf("%d\n",ans[i]);
return 0;
}
[UOJ#12]【UER #1】猜数
给定 \(g,l\),满足 \(gl\) 为完全平方数。
求使得 \(a+b\) 最小,最大的 \(a,b\),满足 \(ab=gl,g|a,g|b\)。
保证有解。
\(1\le g,l\le 10^{18}\)。
不妨设 \(a=gx,b=gy\)。
那么显然可以化为 \(ab=g^2xy=gl\)。
解得 \(xy=\frac{l}{g}\)。
题目保证有解,故 \(g|l\),又因为 \(gl\) 为完全平方数,不难发现 \(\frac{l}{g}\) 也为完全平方数。
那么根据小学学过的“积一定,差小和小”的定理,当 \(x=y=\sqrt{\frac{l}{g}}\) 时 \(g(x+y)\) 最小,\(x=1,y=\frac{g}{l}\) 时 \(g(x+y)\) 最大。
Code
#include <bits/stdc++.h>
typedef long long ll;
ll l,r;
void work() {
scanf("%lld %lld",&l,&r);
printf("%lld %lld\n",l * 2ll * (ll)std::ceil(std::sqrt(r / l)),l * (1 + (r / l)));
return ;
}
int main() {
int T;
scanf("%d",&T);
while(T --)work();
return 0;
}
[UOJ#278]【UTR #2】题目排列顺序
给定 \(f_{1\sim n}\),构造一个 \(1\sim n\) 的排列 \(a\),使得 \(f_i\) 恰为 \(a_i\) 为末尾时的 LIS 长度。
\(1\le n\le 10^5\)。
我用一种很神奇的方法过掉了,暂时无法给出证明。
首先发现,\(n\) 的位置可以尝试找找。
然后对着样例看,\(n\) 恰好在 \(f_i\) 最大且 \(i\) 最小的位置上。
依次类推 \(n-1\sim 1\),发现这样构造一个数列就能符合要求。
然后写一下发现。。就 A 了。
玄学(确信
Code
#include <bits/stdc++.h>
#define pb emplace_back
const int maxn = 1e5 + 5;
int n,f[maxn],a[maxn];
std::vector<int> G[maxn];
int main() {
scanf("%d",&n);
for(int i = 1;i <= n;++ i)scanf("%d",&f[i]),G[f[i]].pb(i);
for(int i = n,now = n;i;-- i) {
for(auto& v : G[i])a[v] = now --;
}
for(int i = 1;i <= n;++ i)printf("%d ",a[i]);
return 0;
}
标签:pos,std,le,10,int,29,whk,maxn,2022.8
From: https://www.cnblogs.com/Royaka/p/16663496.html