1. 接水
题解:
对于30%的数据,可以直接枚举时间,计算每一各单位时间内每个人剩余的接水时间,有离开的话让下一个人接着来接。
对于50%的数据,不难发现,问题可以转化为区间最值的问题,每次从m个人里面找一个用时最少的,让下一个在这里继续排队。
对于100%的数据,可以用数据结构维护区间最值,最终复杂度O(n log m)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m, t, ans;
ll read(){
char c;
bool ok = 0;
ll num = 0;
while(c = getchar())
if(c <= '9' && c >= '0')
ok = 1, num = num*10 + c-'0';
else if(ok) return num;
}
priority_queue <ll, vector<ll>, greater<ll> > q1;
int main(){
freopen("water.in","r",stdin);
freopen("water.out","w",stdout);
while(!q1.empty()) q1.pop();
scanf("%lld%lld", &n, &m);
for(int i = 1; i <= m; i ++) t = read(), q1.push(t), ans = max(t, ans);
for(int i = m+1; i <= n; i ++){
scanf("%lld", &t);
t += q1.top();
q1.pop();
q1.push(t);
ans = max(t,ans);
}
printf("%lld", ans);
return 0;
}
2. 施肥
题解:
对于30%的数据,枚举每一个询问的子矩阵,每次找出区间里的最大高度,和现在子矩阵内的元素之和,找出所有子矩阵的情况取最小值。
对于50%的数据,使用二维前缀和优化求和部分,单调队列维护求最值,复杂度O(n m^2 q)。
对于100%的数据,使用二维单调队列优化取最值得部分O(nm)预处理所有小区间的最值,先用单调队列处理每一行的最值,在处理每一列的最值,最终复杂度O(nmq)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 1e17;
ll a1[1010][1010], sum[1010][1010], mx1[1010][1010], mx2[1010][1010];
ll f[1010];
int n, m, q;
inline ll read(){
char c;
bool ok = 0;
ll num = 0;
while(c = getchar())
if(c <= '9' && c >= '0')
ok = 1, num = num*10+c-'0';
else if(ok) return num;
}
int main(){
freopen("fertile.in","r",stdin);
freopen("fertile.out","w",stdout);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
a1[i][j] = read();
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a1[i][j];
scanf("%d", &q);
for(int i1 = 1; i1 <= q; i1 ++){
int a, b;
scanf("%d%d", &a, &b);
for(int i = 1; i <= n; i ++){
int head = 0, last = 1;
for(int j = 1; j <= m; j ++){
while(head >= last && a1[i][f[head]] < a1[i][j]) head --;
f[++head] = j;
while(f[head] - f[last] >= b) last ++;
mx1[i][j] = a1[i][f[last]];
}
}
for(int j = 1; j <= m; j ++){
int head = 0, last = 1;
for(int i = 1; i <= n; i ++){
while(head >= last && mx1[f[head]][j] < mx1[i][j]) head --;
f[++head] = i;
while(f[head] - f[last] >= a) last ++;
mx2[i][j] = mx1[f[last]][j];
}
}
ll mn = inf;
for(int i = a; i <= n; i ++){
for(int j = b; j <= m; j ++){
mn = min(mn, mx2[i][j]*a*b-(sum[i][j]-sum[i-a][j]-sum[i][j-b]+sum[i-a][j-b]));
}
}
printf("%lld\n", mn);
}
return 0;
}
3. 车站
题解:
对于30%的数据,每次两边bfs求数的直径,在直径上枚举中点的位置。
对于70%的数据,树形动态规划,求出以每个点为根的子树的直径,可以通过向下维护的最长链和次长链,求出直径,并且维护直径端点的位置。
对于100%的数据,从最长链的底部端点的位置,向上倍增,求中点的位置,最终复杂度O(n log n)。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 600010;
const int MX = 20;
int _first[maxn], _next[maxn<<1], _dis[maxn<<1], _to[maxn<<1], _cnt;
int up[maxn][MX+5], end_to[maxn], mx[maxn], d[maxn], r[maxn];
int dis_root[maxn];
void add(int a, int b, int c){
_next[++_cnt] = _first[a];
_first[a] = _cnt;
_to[_cnt] = b;
_dis[_cnt] = c;
}
void dfs1(int now){
for(int i = 1; i <= MX; i ++)
up[now][i] = up[up[now][i-1]][i-1];
for(int i = _first[now]; i; i = _next[i]){
if(up[now][0] != _to[i]){
up[_to[i]][0] = now;
dis_root[_to[i]] = dis_root[now] + _dis[i];
dfs1(_to[i]);
}
}
}
void dfs2(int now){
int mx2 = 0;
end_to[now] = now;
for(int i = _first[now]; i; i = _next[i]){
if(_to[i] == up[now][0]) continue;
dfs2(_to[i]);
if(d[_to[i]] > d[now]){
d[now] = d[_to[i]];
r[now] = r[_to[i]];
}
if(mx[_to[i]] + _dis[i] > mx[now]){
mx2 = mx[now];
mx[now] = mx[_to[i]] + _dis[i];
end_to[now] = end_to[_to[i]];
}else if(mx[_to[i]] + _dis[i] > mx2){
mx2 = mx[_to[i]] + _dis[i];
}
}
if(mx[now] + mx2 > d[now]){
d[now] = mx[now] + mx2;
int z = end_to[now];
for(int i = MX; i >= 0; i --)
if(up[z][i] != 0 &&
d[now]-(dis_root[end_to[now]] - dis_root[up[z][i]]) > d[now]/2)
z = up[z][i];
r[now] = min(d[now] - dis_root[end_to[now]] + dis_root[z],
dis_root[end_to[now]] - dis_root[up[z][0]]);
}
}
int main(){
freopen("station.in","r",stdin);
freopen("station.out","w",stdout);
int n;
scanf("%d", &n);
for(int i = 1; i < n; i ++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
add(b, a, c);
}
dfs1(1);
dfs2(1);
for(int i = 1; i <= n; i ++)
printf("%d\n", r[i]);
return 0;
}