Loading

2022.8.29~2022.9.6 whk 记录

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}\) 也为完全平方数。

那么根据小学学过的“积一定,差小和小”的定理(upd:其实是均值不等式→_→,当时写这篇文章的时候学校还没讲到这),当 \(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;
} 
posted @ 2022-09-06 22:02  Skylakes  阅读(37)  评论(0)    收藏  举报