Luogu CYJian的水题大赛[第二弹] T1,T2___推理+离线模拟

#T1:
这里写图片描述
$ 1⩽A⩽B⩽2∗10^7$

#分析:
我们先将一开始的 i = A i=A i=A的情况下的所有数写下来,看成一个序列
− A / 1 + A / 2 − … … + ( − ) A / A -A/1+A/2-……+(-)A/A A/1+A/2+()A/A
然后我们发现,对于 A + 1 A+1 A+1而言,
写下的序列与上一个不同的地方就是在 A + 1 A+1 A+1的约数的位置,都比 A A A中序列多了或者少了一个这个 1 1 1
对于 A + 2 A+2 A+2,我们同理也能发现是在 A + 1 A+1 A+1序列中,变化了 A + 2 A+2 A+2的约数
那么我们就可以考虑,
先将 A A A的序列处理出来,然后总和设为 x x x,那么则答案必有 ( B − A + 1 ) ∗ x (B-A+1)*x (BA+1)x
然后就是要处理所有数 i i i到数 i + 1 i+1 i+1对答案的贡献,
则我们可以枚举约数 z z z
则我们考虑 z z z对答案的贡献,
显然 [ A . . B ] [A..B] [A..B]中所有数都有 A / z A/z A/z
然后设 C C C为大于 A A A中最小的 z z z的倍数,
可以发现 [ C . . B ] [C..B] [C..B]中所有的数写成序列,在 z z z这个位置,则都有 A / z + 1 A/z+1 A/z+1
然后 [ C + z . . B ] [C+z..B] [C+z..B]中所有的数序列,在 z z z这个位置,则都有 A / z + 2 A/z+2 A/z+2
后面同理,
那么我们可以发现这样的话,对于约数 z z z [ A . . B ] [A..B] [A..B]中答案的贡献,可以通过推理O(1)算出,然后这个贡献,因为已经全部减去了 ( B − A + 1 ) ∗ ( A / z ) (B-A+1)*(A/z) (BA+1)(A/z)
z z z对答案还存在的贡献为 1 + 2 + 3 + … … + [ A + 1.. B ] 中 所 有 的 z 的 倍 数 的 个 数 1+2+3+……+[A+1..B]中所有的z的倍数的个数 1+2+3++[A+1..B]z
这个可以通过等差数列O(1)算出
一开始用A造了个序列, 时 间 复 杂 度 O ( A ) 时间复杂度O(A) O(A)
最后枚举了约数, 时 间 复 杂 度 O ( B ) 时间复杂度O(B) O(B)
时 间 复 杂 度 : O ( A + B ) 时间复杂度:O(A+B) O(A+B)
#代码:

#include
#include
#include
#include
#include

using namespace std;

typedef long long ll;

int A, B;

int main()
{
	scanf("%d %d", &A, &B);
	ll ans = 0;
	for (int i = 1; i <= A; i++) 
	     ans = ans + (A / i) * ((i % 2) ? -1 : 1);
	   
	ans *= (B - A + 1);
	
	for (int i = 1; i <= B; i++) 
	{
		 ll C = A / i * i;
		 if (C <= A) C += i;
		 if (C > B) continue;
		 ll D = (B - C + 1);
		 ll E = (B - C) / i;
		 D = D + D * E;
		 E = (double)((1 + E) * E / 2);
		 E *= i;
		 ans += ((D - E) * ((i % 2) ? -1 : 1));
	} 
	printf("%lld", ans);
	return 0;
}

#T2:
#题目大意:
Luogu CYJian的水题大赛[第二弹] T1,T2___推理+离线模拟_第1张图片
这里写图片描述
#分析:
对于任意两条直线 k 1 x + b 1 k_1x+b_1 k1x+b1 k 2 x + b 2 k_2x+b_2 k2x+b2,如果他们相交,即 k k k不相同,
则他们的交点显然为 ( b 2 − b 1 k 1 − k 2 , k 1 b 2 − b 1 k 1 − k 2 + b 1 ) (\frac{b_2-b_1}{k_1-k_2},k_1\frac{b_2-b_1}{k_1-k_2}+b_1) k1k2b2b1k1k1k2b2b1+b1
我们离线操作排序 X = A j X=A_j X=Aj,即保证 X = A j X=A_j X=Aj的递增,
先行求出 X = m i n ( A j ) X=min(A_j) X=min(Aj)时,所有直线与它的相交的点的大小情况,然后求出一开始的顺序,
A j A_j Aj升序排列后的数为 B 1 , B 2 . . . B M B_1,B_2...B_M B1B2...BM
如果我们求出了 X = B i X=B_i X=Bi时相交点的顺序,那么此时这些点在 X = B i 到 X = B i + 1 X=B_i到X=B_{i+1} X=BiX=Bi+1的区间里,如果存在有点使得任意2条直线相交,则这两条直线的大小关系就会分别加减1。
所以我们线处理出两两直线间的交点然后离线即可,注意细节。
时间复杂度最坏是 O ( M + N 2 ) O(M+N^2) O(M+N2)
#代码:

#include
#include
#include
#include
#include
#define M 500005
#define N 2005

using namespace std;
 
typedef long double ld;
typedef long long ll;

struct Node { ld x; int CF, DF; }A[N*N];
struct Text { ld ask; int id; }C[M];
struct Code { ld y; int K, B; }G[N];
int Rank[N], Now[N], cnt, n, m, k;
ll Ans[M];

bool cmp(Text aa, Text bb)
{
    return aa.ask < bb.ask;	
}

bool cmp1(Code aa, Code bb)
{
	return aa.y < bb.y;
}

bool cmp2(Node aa, Node bb)
{
	return aa.x < bb.x;
}

int main()
{
	scanf("%d %d %d", &n, &m, &k);
    for (int i = 1; i <= n; i++)
         scanf("%d %d", &G[i].K, &G[i].B); 
         
    for (int i = 1; i <= m; i++)
    { 
         scanf("%llf", &C[i].ask);
         C[i].id = i;
    }
    sort(C + 1, C + m + 1, cmp);
    
	for (int i = 1; i <= n; i++) 
    	 G[i].y = G[i].K * C[1].ask + G[i].B, Rank[i] = i, Now[i] = i;	
	sort(G + 1, G + n + 1, cmp1); 
	
	Ans[C[1].id] = G[k].y;
	
	for (int i = 2; i <= n; i++)
		 for (int j = i - 1; j >= 1; j--)
		      if (G[i].K != G[j].K)
		      {
		          ld X_C = (G[j].B - G[i].B) / (G[i].K - G[j].K);
		          if (X_C > C[1].ask) 
		              A[++cnt].x = X_C, A[cnt].CF = i, A[cnt].DF = j;
		      }
		      
	sort(A + 1, A + cnt + 1, cmp2);
	
    int l = 0;
    for (int i = 2; i <= m; i++)
    {
    	 while (A[l + 1].x <= C[i].ask && l < cnt)
		 {
    	        l++;
    	        Now[--Rank[A[l].CF]] = A[l].CF;
    	        Now[++Rank[A[l].DF]] = A[l].DF;
    	 }
    	 Ans[C[i].id] = G[Now[k]].K * C[i].ask + G[Now[k]].B;
    }
    for (int i = 1; i <= m; i++) printf("%lld\n", Ans[i]);
	return 0;
}

你可能感兴趣的:(暴力/枚举/模拟,规律与思维)