C - Anti-Division(ATC经典题型 + lcm)
Description:
给你一段区间[a, b] (<= 1e18),请问区间中有多少数字是满足又不被c整除,又不被d整除
Solution:
由于区间范围大,不可能暴力枚举。我们直接容斥一下,找区间中被c整除和被d整除的个数
容易想到用区间长度分别除c和d得到个数,但是仅仅对[1, x]能这样做,我们就用一下前缀和的思想[a, b] = [1, b] - [1, a - 1]就好,发现样例过不了,观察发现原来是发生了重复计算,比如2和3都被6整除,本来贡献应该为1,但是这样算的话,贡献为2了。我们就找一下c和d的最小公倍数,然后得到区间中重复计算过的被lcm的倍数所整除的个数,减去这个数就是正解了
Code:
int main()
{
cin >> a >> b >> c >> d;
LL res1 = b - (b / c + b / d - b / lcm(d, c));
LL res2 = (a - 1) - ((a - 1) / c + (a - 1) / d - (a - 1) / lcm(d, c));
cout << res1 - res2;
}
E - Friendships(图论 + 构造)
Description:
给出n和k,表示无向联通图中有n个点,你可以任意连边,输出能构造出k条两点间最短路为2的图的连边方案。
Solution:
需要从图的性质入手,无向连通图意味着图中最少需要有n - 1条边才能保证联通。如果连接一条边的话,对两个端点的影响就是使两个端点的最短路长度为1了。可以推得:边数越多,最短路长度为2的路越少。考虑一种卫星式的连接方案,此时最短路为2的边数有(n - 1) * (n - 2) / 2条,这是k的最大值,若k大于该值,必然不可能构成,在这个方案的基础上,我们来减少最短路为2的路的数量。由于连接一条边,可以使两个端点最短路长度为1,我们就找两个最短路长度为2的端点来连接。为了便于输出不同的方案,从1和其他边开始连边,连到n - 1的时候,将1变成2继续连边,以此类推。
Code:
int n, k;
struct node {
int u, v;
node(){}
node(int a, int b):u(a), v(b){}
};
vector<node> g;
int main()
{
cin >> n >> k;
int m = (n - 1); //若要保证图的连通性,最少需要n - 1条边
//因为一条边会使一条路径长度变成1 所以边越多 长度为2的路径越少
if(n == 0)
{
cout << -1;
return 0;
}
int sum = m * (m - 1) / 2; //以一个点为中心点,其他店全部直接与其相连,可以得到最多的长度为2的边数
if(k > sum)
{
cout << -1;
return 0;
}
for(int i = 1; i <= m; i ++)
g.push_back(node(i, n)); //(n - 1)(n - 2) / 2条边
int i = 1, j = 2;
while(sum != k) //连一条边,减少一个答案
{
sum --;
g.push_back(node(i, j));
j ++;
if(j == m + 1) i ++, j = i + 1;
}
cout << g.size() << '\n';
for(auto x : g)
cout << x.u << ' ' << x.v << '\n';
}