记 2024.4.17

P3044

[USACO12FEB] Relocation S

题目描述

Farmer John is moving! He is trying to find the best place to build a new farm so as to minimize the amount of travel he needs to do each day.

The region to which FJ plans to move has N towns (1 <= N <= 10,000). There are M bi-directional roads (1 <= M <= 50,000) connecting certain pairs of towns. All towns are reachable from each-other via some combination of roads. FJ needs your help selecting the best town as the home for his new farm.

There are markets in K of the towns (1 <= K <= 5) that FJ wants to visit every day. In particular, every day he plans to leave his new farm, visit the K towns with markets, and then return to his farm. FJ can visit the markets in any order he wishes. When selecting a town in which to build his new farm, FJ wants to choose only from the N-K towns that do not have markets, since housing prices are lower in those towns.

Please help FJ compute the minimum distance he will need to travel during his daily schedule, if he builds his farm in an optimal location and chooses his travel schedule to the markets as smartly as possible.

FJ决定搬家,重新建设农场,以便最小化他每天的行程。

FJ搬往的区域有N(1 <= N <= 10,000)个城镇,共有M (1 <= M <= 50,000)条双向道路连接某些城镇,所有城镇都能找到互通路线。

有K (1 <= K <= 5)个城镇建有市场,FJ每天离开新农场后,都要光顾这K个城镇,并返回农场。FJ希望建设农场的城镇不包含市场。

请帮助FJ选择最佳城镇建设农场,使得他每天的行程最小。

输入格式

* Line 1: Three space-separated integers, N, M, and K.

* Lines 2..1+K: Line i+1 contains an integer in the range 1...N identifying the town containing the ith market. Each market is in a different town.

* Lines 2+K..1+K+M: Each line contains 3 space-separated integers, i, j (1 <= i,j <= N), and L (1 <= L <= 1000), indicating the presence of a road of length L from town i to town j.

输出格式

* Line 1: The minimum distance FJ needs to travel during his daily routine, if he builds his farm in an optimal location.

样例 #1

样例输入 #1

5 6 3 
1 
2 
3 
1 2 1 
1 5 2 
3 2 3 
3 4 5 
4 2 7 
4 5 10

样例输出 #1

12

提示

There are 5 towns, with towns 1, 2, and 3 having markets. There are 6 roads.

FJ builds his farm in town 5. His daily schedule takes him through towns 5-1-2-3-2-1-5, for a total distance of 12.

O(n3)O(n^3)

考虑全源最短路,由题面 k5k\le 5 可以想到枚举到市场的顺序,在求枚举农场最短路。这样的算法不能通过该题目,需要改变全源最短路占的时间复杂度。

O(nk!)O(nk!)

不需要全源最短路,因为图中的道路都是双向边,所以来和去的最短路一样,所以只要对每个市场求一遍最短路,因为只会用到每个市场到其他地方的最短路,然后枚举到市场的顺序,在求枚举农场最短路,这样时间复杂度是枚举的时间复杂度。

For about 40 min.

By myself.

Code

#include <iostream>
#include <queue>
#include <cstring> 
#include <algorithm>
using namespace std;
const int N=1e4+10,M=1e5+10;
int h[N],e[M],ne[M],w[M],idx;
int n,m,k;
int kk[6];
int dist[6][N];
int st[N];
int vis[N];
void add(int a,int b,int c)
{
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
void dijkstra(int x)
{
    memset(dist[x],0x3f,sizeof dist[x]);
    memset(st,0,sizeof st);
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
    dist[x][kk[x]]=0;
    q.push({0,kk[x]});
    while(q.size())
    {
        auto t=q.top();
        q.pop();
        int ver=t.second;
        if(st[ver]) continue;
        st[ver]=1;
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[x][j]>dist[x][ver]+w[i])
            {
                dist[x][j]=dist[x][ver]+w[i];
                q.push({dist[x][j],j});
            }
        }
    }
    return;
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m>>k;
    for(int i=1;i<=k;i++) cin>>kk[i],vis[kk[i]]=1;
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        add(x,y,z);
        add(y,x,z);
    }
    for(int i=1;i<=k;i++) dijkstra(i);
    int ans=0x3f3f3f3f;
    vector<int> o(k);
    for(int i=0;i<k;i++) o[i]=i+1;
    do
    {
        for(int i=1;i<=n;i++)
        {
            int sum=0;
            if(vis[i]) continue;
            sum+=dist[o[0]][i]+dist[o[k-1]][i];
            for(int j=1;j<k;j++) sum+=dist[o[j-1]][kk[o[j]]];
            ans=min(ans,sum);
        }
    }
    while(next_permutation(o.begin(),o.end()));
    cout<<ans;
    return 0;
}

CF1900D

Small GCD

题面翻译

a,b,ca, b, c 为整数,定义 f(a,b,c)f(a, b, c) 如下:

将三个数排序,使得 abca \le b \le c。则函数返回 gcd(a,b)\gcd(a, b) , 这里 gcd(a,b)\gcd(a, b) 表示 最大公约数 (GCD) 。简而言之,函数返回较小的两个数的最大公约数。

你会得到数组 aa,包含 nn 个元素。求 f(ai,aj,ak)f(a_i, a_j, a_k) 之和,其中 1i<j<kn1 \le i< j < k \le n

形式化的讲,求 i=1nj=i+1nk=j+1nf(ai,aj,ak) \sum_{i = 1}^n \sum_{j = i+1}^n \sum_{k =j +1}^n f(a_i, a_j, a_k)

【输入格式】

第一行一个整数 t (1t10) t \ ( 1 \le t \le 10 ) 为数据组数。

每组数据第一行为 n (3n8104)n \ (3 \le n \le 8 \cdot 10^4),第二行为 a1,a2,,ana_1, a_2, \ldots, a_n ( 1ai1051 \le a_i \le 10^5 ) 。

所有数据中 nn 的和不超过 81048 \cdot 10^4

【输出格式】

对每组数据,输出题中要求计算的求和。

题目描述

Let aa , bb , and cc be integers. We define function f(a,b,c)f(a, b, c) as follows:

Order the numbers aa , bb , cc in such a way that abca \le b \le c . Then return gcd(a,b)\gcd(a, b) , where gcd(a,b)\gcd(a, b) denotes the greatest common divisor (GCD) of integers aa and bb .

So basically, we take the gcd\gcd of the 22 smaller values and ignore the biggest one.

You are given an array aa of nn elements. Compute the sum of f(ai,aj,ak)f(a_i, a_j, a_k) for each ii , jj , kk , such that 1i<j<kn1 \le i < j < k \le n .

More formally, compute $$$\sum_{i = 1}^n \sum_{j = i+1}^n \sum_{k =j +1}^n f(a_i, a_j, a_k). $$$

输入格式

Each test contains multiple test cases. The first line contains the number of test cases tt ( 1t101 \le t \le 10 ). The description of the test cases follows.

The first line of each test case contains a single integer nn ( 3n81043 \le n \le 8 \cdot 10^4 ) — length of the array aa .

The second line of each test case contains nn integers, a1,a2,,ana_1, a_2, \ldots, a_n ( 1ai1051 \le a_i \le 10^5 ) — elements of the array aa .

It is guaranteed that the sum of nn over all test cases does not exceed 81048 \cdot 10^4 .

输出格式

For each test case, output a single number — the sum from the problem statement.

样例 #1

样例输入 #1

2
5
2 3 6 12 17
8
6 12 8 10 15 12 18 16

样例输出 #1

24
203

提示

In the first test case, the values of ff are as follows:

  • i=1i=1 , j=2j=2 , k=3k=3 , f(ai,aj,ak)=f(2,3,6)=gcd(2,3)=1f(a_i,a_j,a_k)=f(2,3,6)=\gcd(2,3)=1 ;
  • i=1i=1 , j=2j=2 , k=4k=4 , f(ai,aj,ak)=f(2,3,12)=gcd(2,3)=1f(a_i,a_j,a_k)=f(2,3,12)=\gcd(2,3)=1 ;
  • i=1i=1 , j=2j=2 , k=5k=5 , f(ai,aj,ak)=f(2,3,17)=gcd(2,3)=1f(a_i,a_j,a_k)=f(2,3,17)=\gcd(2,3)=1 ;
  • i=1i=1 , j=3j=3 , k=4k=4 , f(ai,aj,ak)=f(2,6,12)=gcd(2,6)=2f(a_i,a_j,a_k)=f(2,6,12)=\gcd(2,6)=2 ;
  • i=1i=1 , j=3j=3 , k=5k=5 , f(ai,aj,ak)=f(2,6,17)=gcd(2,6)=2f(a_i,a_j,a_k)=f(2,6,17)=\gcd(2,6)=2 ;
  • i=1i=1 , j=4j=4 , k=5k=5 , f(ai,aj,ak)=f(2,12,17)=gcd(2,12)=2f(a_i,a_j,a_k)=f(2,12,17)=\gcd(2,12)=2 ;
  • i=2i=2 , j=3j=3 , k=4k=4 , f(ai,aj,ak)=f(3,6,12)=gcd(3,6)=3f(a_i,a_j,a_k)=f(3,6,12)=\gcd(3,6)=3 ;
  • i=2i=2 , j=3j=3 , k=5k=5 , f(ai,aj,ak)=f(3,6,17)=gcd(3,6)=3f(a_i,a_j,a_k)=f(3,6,17)=\gcd(3,6)=3 ;
  • i=2i=2 , j=4j=4 , k=5k=5 , f(ai,aj,ak)=f(3,12,17)=gcd(3,12)=3f(a_i,a_j,a_k)=f(3,12,17)=\gcd(3,12)=3 ;
  • i=3i=3 , j=4j=4 , k=5k=5 , f(ai,aj,ak)=f(6,12,17)=gcd(6,12)=6f(a_i,a_j,a_k)=f(6,12,17)=\gcd(6,12)=6 .

The sum over all triples is 1+1+1+2+2+2+3+3+3+6=241+1+1+2+2+2+3+3+3+6=24 .In the second test case, there are 5656 ways to choose values of ii , jj , kk . The sum over all f(ai,aj,ak)f(a_i,a_j,a_k) is 203203 .

请注意如果有 gcd()gcd() 出现,如果要批量求,一定要考虑先把所有的数因数得到,这样求 gcd()gcd(),将会方便许多,也会快很多,以及别忘了去掉重复的,如果题面中有关于单调的一点东西,都要先考虑是不是可以将序列排序,会不会影响答案。

做这题时,我像 sb

For about 2 hours.

By problems helper.

I'm angry.

Code

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
const int N=1e5+10;
int a[N],f[N],g[N];
vector<int> fac[N];//因数
signed main()
{
   for(int i=1;i<=1e5;i++)
   	for(int j=i;j<=1e5;j+=i) fac[j].push_back(i);//因数
   for(int i=1;i<=1e5;i++) reverse(fac[i].begin(),fac[i].end());//make it from big to small
   int tt;
   cin>>tt;
   while(tt--)
   {
   	memset(g,0,sizeof g);
   	memset(f,0,sizeof f);
   	int n;
   	cin>>n;
   	for(int i=1;i<=n;i++) cin>>a[i];
   	sort(a+1,a+1+n);//不影响原先的答案,但是答案统计需要单调递增
   	int ans=0,tmp=0;
   	for(int i=1;i<=n;i++)
   	{
   		ans+=tmp;//加上之前的所有因为a[i]>=a[1~i-1]
   		for(auto u:fac[a[i]])
   		{
   			f[u]=g[u];//不经修改的因数个数
   			for(auto v:fac[a[i]/u])//枚举a[i]/u的因数 
   				if(v!=1) f[u]-=f[v*u];//a[i]%v*u==0,u*v<=a[i] 去除u的倍数与目前的重复
   			tmp+=f[u]*u;//u值乘个数
   		}
   		for(auto u:fac[a[i]]) g[u]++;//把a[i]因数u出现次数+1
   	}
   	cout<<ans<<endl;
   }
   return 0;
}
posted @ 2024-04-17 18:14  PM_pro  阅读(22)  评论(0)    收藏  举报  来源