国庆集训Day2
A. 小灯笼
题目描述
一条平直的公路上有(n)个小路灯,第(i)个路灯的坐标是(a_i)。小A需要把其中的(k)个点亮,使得每个小路灯与距离最近的被点亮的小路灯的距离的最大值最小。求这个最小值。
输入格式
第(1)行(2)个正整数(n,k)。
接下来(n)行,每行一个整数(a_i),表示第(i)个小路灯的坐标,保证(a_i)是严格单调递增的。
输出格式
(1)行(1)个数表示答案。
样例数据
input
6 3
5
6
12
19
20
27
output
6
30分:
不妨设(dp[i,j])表示前(i)个小路灯其中第(i)个亮,总共j个。
(dp[i,j]=dp[l,j-1]+g(l+1,i))。
100分:二分
一看题面,最小化最大值就知道这极有可能是二分。
框架不难,按照贪心原则,当前灯能不亮就不亮。
C++ AC代码
#include<iostream>
#include<cstdio>
using namespace std;
const int SIZE = 100000 + 5, INF = 1 << 30;
int n, k;
int a[SIZE];
bool valid(int x)
{
int last = -INF, cnt = 0;
for(int j = 1; j <= n; ++ j)
{
if(a[j] - last <= x) continue;
int i = j;
++ j;
while(j <= n)
{
if(a[j] - a[i] > x)
{
++ cnt;
last = a[j - 1];
-- j;
break;
}
++ j;
}
}
if(a[n] - last > x) ++ cnt;
if(cnt <= k) return true;
return false;
}
int main()
{
scanf("%d %d", &n, &k);
int L = INF, R = -1, mid;
for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
R = a[n] - a[1];
for(int i = 2; i <= n; ++ i) L = min(L, a[i] - a[i - 1]);
while(L < R)
{
mid = L + ((R - L) >> 1);
if(valid(mid)) R = mid;
else L = mid + 1;
}
printf("%d
", L);
return 0;
}
B. 序列
题目描述
设数列(P=[p_1,p_2,……,p_n]),定义(f(p,k)=[p_2,p_3,……,p_k,p_1,p_{k+2},p_{k+3},……,p_{2k},p_{k+1},……]),也就是把(P)每(k)个分成一段(最后如果不足(k)个,把它们分到新的一段),然后将每段的首个元素移动到该段末尾。求(f(f(……f(f([1,2,……,n],2),3),……),n))。
输入格式
(1)行(1)个正整数(n)。
输出格式
(1)行(n)个数表示答案。
样例数据
input
4
output
4 2 3 1
此题实在是思维好题!
首先,我们可以观察整个数组改变的位置。
明显,我们可以把每次操作中不是首项的数不动,其他的数位置(+i)。
C++ AC代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 1000000 + 5;
int n, p[maxn << 1];
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) p[i] = i;
int delta = 0;
for(int i = 2; i <= n; ++ i)
{
int cur = 0;
for(int j = delta + 1; j <= n + delta; j += i) swap(p[j], cur);
++ delta;
swap(p[n + delta], cur);
}
for(int i = n; i < n << 1; ++ i) printf("%d ", p[i]);
return 0;
}
C. 路灯
题目描述
一条平直的公路上有(n)个路灯,第(i)个路灯的坐标是(a_i)。小A需要把其中的(k)个点亮,使得每个路灯与距离最近的被点亮的路灯的距离和最小。求这个最小值。
输入格式
第(1)行(2)个正整数(n,k)。
接下来(n)行,每行一个整数(a_i),表示第(i)个路灯的坐标,保证(a_i)是严格单调递增的。
输出格式
(1)行(1)个数表示答案。
样例数据
input
6 3
5
6
12
19
20
27
output
8
100分 DP
考虑(dp[i,j,k])代表([i,j])中点亮了(k)盏路灯的最小值。
则:(dp[i,j,k]=min(dp[i,k,x]+dp[k+1,j,k-x]))。
初始化:(dp[i,j,1]=min(a[k]*(k-i)-s[k-1]+s[i-1]+s[j]-s[k]-(j-k)*a[k]))。
C++ AC代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 200 + 5, maxk = 55;
int n, k, a[maxn], s[maxn] = {}, dp[maxn][maxn][maxk];
int main()
{
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
for(int i = 1; i <= n; ++ i) s[i] = s[i - 1] + a[i];
memset(dp, 0x3f, sizeof(dp));
for(int i = 1; i <= n; ++ i)
{
for(int j = i; j <= n; ++ j)
{
for(int p = i; p <= j; ++ p)
{
dp[i][j][1] = min(dp[i][j][1], (p * 2 - i - j) * a[p] - s[p - 1] + s[i - 1] + s[j] - s[p]);
}
}
}
for(int len = 2; len <= n; ++ len)
{
int i = 1, j = len;
for(; j <= n; ++ i, ++ j)
{
for(int l = 2; l <= k; ++ l)
{
for(int p = 1; p < l; ++ p)
{
for(int x = i; x <= j; ++ x)
{
dp[i][j][l] = min(dp[i][j][l], dp[i][x][p] + dp[x + 1][j][l - p]);
}
}
}
}
}
printf("%d
", dp[1][n][k]);
return 0;
}
DP(正解)
我们设(dp[i,j])为选点亮(i)个灯共亮了(j)个
D. 匹配
题目描述
给定一颗(n)个点(n−1)条边的树,每条边的长度都是(1),(i)号节点的权是(a_i)。如果三个节点两两在树上的距离都是(4),那么称这三个节点构成了一个“组合”,一个“组合”的权是三个节点的权的乘积。求所有“组合”的权的和。
输入格式
第(1)行一个整数(n)。
接下来(1)行(n)个正整数,第(i)个数表示(a_i)。
接下来(n−1)行,每行(2)个正整数(u,v),表示(u)和(v)间有一条边。保证输入的是一颗树。
输出格式
(1)行(1)个数表示答案。
样例数据
input
7
1 2 3 4 5 6 7
1 2
2 3
1 4
4 5
1 6
6 7
output
105
这道题实际上是树上的基本应用。
我们很容易知道:一个“组合”成立,当且仅当这三个点(x,y,z)距离公共点为(2)。
DP暴力求解,时间复杂度为(O(n))!
C++ AC代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
const int SIZE = 100000 + 5;
vector <int> G[SIZE];
int n, a[SIZE];
long long f[SIZE] = {}, fa[SIZE] = {}, ans = 0;
void dfs(int u, int Fa)
{
fa[u] = Fa;
for(int i = 0; i < G[u].size(); ++ i)
{
int v = G[u][i];
if(v == Fa) continue;
f[u] += a[v];
dfs(v, u);
}
return;
}
void solve(int u)
{
long long p1 = 0, p2 = 0, p3 = 0;
for(int i = 0; i < G[u].size(); ++ i)
{
int v = G[u][i];
if(v != fa[u])
{
solve(v);
p3 += p2 * f[v];
p2 += p1 * f[v];
p1 += f[v];
}
else
{
long long val = f[v] - a[u] + a[fa[v]];
p3 += p2 * val;
p2 += p1 * val;
p1 += val;
}
}
ans += p3;
return;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) scanf("%lld", &a[i]);
for(int i = 1; i < n; ++ i)
{
int u, v;
scanf("%d %d", &u, &v);
G[u].push_back(v), G[v].push_back(u);
}
dfs(1, 0);
solve(1);
printf("%lld
", ans);
return 0;
}