清北学堂周末刷题班第五场

A. 大大大

Illyasviel:"两个数乘起来会比一个数大吗?"

Star-dust:"不知道啊,来算算吧。"

读入一个\(n\),对于一个三元组\((i,j,k)\)满足要求当且仅当\(1≤i,j,k≤n\)\(i×j≥k\)

输入描述:

一行一个数字\(n\)

输出描述:

一行一个\(ans\)表示满足要求的三元组的个数。

输入样例:

10

输出样例:

900

数据范围:

对于\(30\%\)的数据\(n≤100\)

对于\(60\%\)的数据\(n≤5000\)

对于\(100\%\)的数据\(n≤100000\)


大致有这样:

我们可以先枚举\(k\),然后对于每一个\(i\),我们进行计算,\(O(nlogn)\)\(O(n\sqrt n)\),我实现的是\(n\sqrt n\)的。(有巨佬会数论分块\(O(n)\)\(O(\sqrt n))\)的教一下我)。

#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
int n, sqr[1000000];
long long cnt = 0;
void read(int &x)
{
	char ch = getchar();
	bool mark = false;
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') mark = true;
	for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';	
	if(mark) x = -x;
	return;
}
int main()
{
	read(n);
	for(int i = 1; i <= n; ++ i) sqr[i] = ceil(sqrt(i));
	for(int k = 1; k <= n; ++ k)
	{
		cnt += (long long)(n - sqr[k] + 1) * (n - sqr[k] + 1);
		for(int j = 1; j < sqr[k]; ++ j) cnt += (long long)(n - ceil((double)k / j) + 1) * 2ll;
	}	
	printf("%lld\n", cnt);
	return 0;
}

B. kkk

Star-dust:"你会最短路吗?"

Illyasviel:"当然!"

Star-dust:"那你来求一求这k个关键点中是否存在长度%P为L的路径吧。"

Illyasviel:"这和最短路有什么关系吗?"

Star-dust:"不知道啊~"

输入描述:

第一行一个数字\(T\)代表数据组数。

对于每个数据,第一行五个数\(n,m,k,P,L\)表明有\(n\)个点,\(m\)条边,\(k\)个在图中的点。

接下来一行\(k\)个数代表关键点。

接下来\(m\)行每行三个数\(x,y,z\)表示\(x\)\(y\)之间有一条长度为\(z\)的路径。(图为无向联通图)

输出描述:

输出\(T\)行,当存在一条路径从起点和终点都在\(k\)个点中输出"YES",否则输出"NO"(不包含引号)。

输入样例:

1
2 2 2 5 3
1 2
1 2 1
2 1 1

输出样例:

YES

样例解释:

1-2-1-2

数据范围:

对于\(40\%\)的范围\(T≤500,0≤L,z≤P≤20,k≤n≤m≤500,k≤10\)

对于\(80%\)的范围\(T≤500,0≤L,z≤P≤20,k≤n≤m≤500\)

对于\(100\%\)的范围\(T≤500,0≤L,z≤P≤109,k≤n≤m≤500\),\(P\)是奇数。


这道题思维好题。

这道题看起来是一道图论题,实际上跟图论没什么关联。

由于是张无向图,所以我们可以通过反复走一条边来修正路径\(\%P\)的取值。

假设存在一条长度为\(l\)的路径,我们可以来回的长度一定为\(2*k*l\)(当然也可以不往返走),在\(\%P\)意义下相当于走了\(k*gcd(P, l)\)

进一步,我们可以通过反复走其他路径来修正路径\(\%P\)取值。

综上,我们能够走的取值一定是\(k*gcd(P,w_1,w_2,..,w_n)\),判断\(L\)是都为\(gcd(P,w_1,w_2,..,w_n)\)倍数即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int n, m, k, p, l; 
int gcd(int x, int y)
{
	if(!y) return x;
	return gcd(y, x % y);
}
int main()
{
	int T;
	scanf("%d", &T);
	while(T --)
	{
		scanf("%d %d %d %d %d", &n, &m, &k, &p, &l);	
		int u, v, tmp;
		for(int i = 1; i <= k; ++ i)
		{
			scanf("%d", &tmp);
		}
		for(int i = 1; i <= m; ++ i)
		{
			scanf("%d %d %d", &u, &v, &tmp);
			p = gcd(p, tmp);
		}
		if(l % p == 0) puts("YES");
		else puts("NO");
	}
	return 0; 
}

C. A的B次方

Illyasviel:"今天我们学了\(A^B\)?"

Star-dust:"我们也学了诶!"

Star-dust:"那我来考考你吧!"

已知\(A\)\(P\),求任意一个不等于\(A\)且小于\(2×10^{18}\)\(B\)使得\(A^B≡B^A(\mod P)\)

输入描述

一行输入两个整数\(A\)\(P\)

输出描述

输出任意一个满足要求的数字\(B\)

\(B\)要为一个不大于\(2×10^{18}\)的正整数。

样例输入

78 100

样例输出

16

数据范围

对于\(30\%\)的数据:

\(1≤A,P≤1000\)

对于\(30\%\)的数据:\(P\)为质数

对于\(100\%\)的数据:\(64≤A≤10^9,P≤10^9,1≤B≤10^{18}\)


首先,我们令\(B≡A(mod\ P),B=k*P+A\)

这样:\(A^A≡A^B(\mod P)\)

再由扩展欧拉定理解:

\(B≡A(\mod \phi(P))\)

等价于:

\(B≡k*\phi(P)+A\)

\(k\)等于\(P\),问题得解。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define ull unsigned long long
using namespace std;
int A, P;
int euler(int x)
{
	int res = x;
	for(int i = 2; i <= sqrt(x); ++ i)
	{
		if(x % i == 0)
		{
			res = res / i * (i - 1);
			while(x % i == 0) x /= i;
		}
		if(x == 1) return res;
	}
	if(x > 1) res = res / x * (x - 1);
	return res;
}
int main()
{
	scanf("%d %d", &A, &P);
	cout << (ull)((ull)P * euler(P) + A) << endl;
	return 0;
} 

D. 灯塔

Star-dust:"每个人都是灯塔,灯塔之间相隔万里,无法触碰无法沟通,唯一能做的就是用自己的光去照耀别人。"

Illyasviel:"如果能被某个灯塔一直照耀,那一定很幸福吧。"

Star-dust:"我能成为你的灯塔吗?"

Illyasviel:"好啊~"

海上有着\(n\)个灯塔,第\(i\)个灯塔在位置\(i\)闪耀着,灯塔的光覆盖着\([i−d_i,i+d_i]\)的所有灯塔,对于第\(k\)个灯塔,他想知道有多少个\(i\)满足\(i<k\)且至少存在一个在\(i\)\(k\)中间的灯塔\(j\)满足灯塔\(j\)同时被灯塔\(i\)和灯塔\(k\)照耀,并且\(j\)\(k\)的距离小于等于\(j\)\(i\)之间的距离。

输入描述:

第一行一个整数\(n\)

接下来一行\(n\)个数字,第\(i\)个代表\(d_i\)

输出描述:

一行一个答案\(ans\)

\(f_k\)表示对于第\(k\)个灯塔有多少个灯塔满足条件。

\(ans\)\(n\)\(f_k\)的异或和。

样例输入:

10
2 2 3 2 3 2 3 3 3 1

样例输出:

2

样例解释:

对应位置答案分别为0 0 1 2 3 3 3 4 4 2

数据范围:

对于\(20\%\)的数据:\(n≤100\)

对于\(20\%\)的数据:\(n≤5000\)

对于\(20\%\)的数据:\(d_i\)完全相同

对于\(20\%\)的数据:\(n≤100000\)

对于\(100\%\)的数据:\(n≤3000000\),\(1≤d_i≤n\)


仔细思考不难发现:对于\(k\),对于满足条件\(i\)需要满足:

  • \(i+d_{i}≥k-d_{k}\)

  • \(j≤i+d_{i}\)

  • \(i+k≤2*j\)

由于\(j\)至少要满足一个,所以\(j\)最大为\(i+d_i\)。以下,我们令\(j=i+d_{i}\)

换句话说,对于任意一个\(i\),我们都有确定的\(j\)

因此,对于第一个条件,等价于:

  • \(j≥k-d_k\)

这启发:所有的\(j\)只要在\(k-d_k\)以上,都满足第一个条件。

第二个条件已经用完了。

第三个条件,是限制所有满足第一个条件的基础上的一个条件,换句话说,满足第一个条件之后,还要满足第三个条件\(i\)才满足题意

将第三个式子变形:

  • \(k≤2*j-i\)

容易看出,对所有满足第一个条件的\((i,j)\)而言,只对所有满足该条件的\(k\)产生影响,对于后面不满足的\(k\)没有影响。

具体地,我们关于值域建立一颗线段树,用以维护每个位置上\(j\)的个数

对于每一个\(k\),对它统计的时候,我们需要将所有的\(k-d_k\)\(n\)上的数统计\(j\)个数。因为探讨过,对于每一个\(i\),它有唯一确定的\(j\),所以\(j\)的个数就是满足题意的\(i\)的个数。

紧接着,对于第三个条件,对于\(2*j-i<k\)来说,\(i\)是有可能满足第一个条件的,我们刚刚统计的和需要删除的。

又由于该\(i\)对于后续的\(k\)没有影响了,所有在遍历\(i\)的时候,顺便把它不满足要求的\(k\)中记录一下,所有当遍历到\(k\)的时候,将\(j\)对应的权值\(-1\)。通过这样做,就可以满足第三个条件了。

由于必须是\(i<k-1\),所以,我们正序扫一遍,同时添加,同时删除。

该算法貌似会T。将线段树改为树状数组即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cstdio>
using namespace std;
const int maxn = 3000000 + 5; 
vector <int> p[maxn];
int n, ans = 0, d[maxn], c[maxn], f[maxn] = {};
void read(int &x)
{
	bool mark = false;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') mark = true;
	for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	if(mark) x = -x;
	return;
}
inline int lowbit(int x)
{
	return x & (-x);
}
void add(int x, int v)
{
	x = n - x + 1;
	while(x <= n)
	{
		c[x] += v;
		x += lowbit(x);
	}
	return;
}
int ask(int x)
{
	int res = 0;
	x = n - x + 1;
	while(x)
	{
		res += c[x];
		x -= lowbit(x);
	}
	return res;
}
int main()
{
	read(n);
	for(int i = 1; i <= n; ++ i) read(d[i]);
	for(int k = 3; k <= n; ++ k)
	{
		int i = k - 2, j = i + d[i], l = min(n + 1, 2 * j - i + 1);
		int cp = min(n, j);
		add(cp, 1);
		p[l].push_back(j);
		for(int q = 0; q < p[k].size(); ++ q) add(p[k][q], -1);
		f[k] = ask(max(1, k - d[k]));
	}
	for(int i = 3; i <= n; ++ i) ans ^= f[i];
	printf("%d\n", ans);
	return 0;
}
posted @ 2020-11-03 12:24  大秦帝国  阅读(113)  评论(0)    收藏  举报