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


    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(nsqrt n)),我实现的是(nsqrt 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
    ", 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
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    sql 从A表复制数据到B表
    sql union和union all
    sql 类型转换
    SQL聚合函数
    数据存储类型
    asp.net中XML如何做增删改查操作(基础操作)
    数据库分页总结
    javascript 和 jquery 初学总结
    File FileStream StreamReader和StreamWriter
    oracle建数据库
  • 原文地址:https://www.cnblogs.com/zach20040914/p/13919607.html
Copyright © 2020-2023  润新知