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;
}