4- noip交流赛 DAY2

来源:互联网 发布:布瑞克农业数据库 编辑:程序博客网 时间:2024/05/21 04:20

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


1 0
原创粉丝点击