本次比赛题解:戳这里 ——> 题解
写在前面
迟到的总结。
不过个人觉得这一次题出的很好(* ̄︶ ̄)。
T1 货物收集
题目传送门:货物收集
题目描述
分析
直接贪心就好,每次选武力值最小的就好,反正答案不会更劣。
题解的做法,二分也可以。
代码
/********************* User:fxyly Language:c++ problem:Nowcoder Algorithm *********************/ #include<bits/stdc++.h> using namespace std; const int maxn = 1e6 + 5; int n,w,size,first[maxn]; long long a[maxn]; bool vis[maxn]; struct Edge{ int v,nt; long long w; }edge[maxn << 1]; template<class T>inline void read(T &x){ x = 0;char ch = getchar();bool flag = 0; while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); if(flag) x = -x; } template<class T>void putch(const T x){if(x > 9) putch(x / 10);putchar(x % 10 | 48);} template<class T>void put(const T x){if(x < 0) putchar('-'),putch(-x);else putch(x);} void eadd(int u,int v,int w){ edge[++size].v = v; edge[size].w = w; edge[size].nt = first[u]; first[u] = size; } void readdata(){ read(n);read(w); for(int i = 2;i <= n; ++ i) read(a[i]); for(int i = 1;i < n; ++ i){ int u,v,w;read(u);read(v);read(w); eadd(u,v,w);eadd(v,u,w); } } void bfs(){ typedef pair<long long,int>pir; long long cur = 0,ans = 0; priority_queue<pir,vector<pir>,greater<pir> >q; q.push(make_pair(0,1)); while(!q.empty()){ int u = q.top().second; long long x = q.top().first;q.pop(); vis[u] = 1;cur += a[u];ans = max(ans,x); if(cur >= w) break; for(int i = first[u];i;i = edge[i].nt){ int v = edge[i].v;long long w = edge[i].w; if(vis[v]) continue; vis[v] = 1; q.push(make_pair(w,v)); } } put(ans); } void work(){ bfs(); } int main(){ // freopen("3.in","r",stdin); readdata(); work(); return 0; }
T2 货物分组
题目传送门:货物分组
题目描述
分析
因为题目中并没有限制箱子的个数,而我们所求的只是个物品的最小的花费。又因为花费的计算方法与箱子数有关:
对于第个箱子,如果里面装的货物总重量为,那么费用为。
注意到是箱子数,说明越到后面的箱子里的物品,就会比前一个多算一遍。这样,我们就可以倒序处理,到物品,就直接加,就可以把花费都算出来,还不用存下箱子的个数
所以我们可以直接将数组降为一维。用表示(从后往前)到第个物品的最小花费
容易知道是从后向前单调递增的,这可以为我们省去不少麻烦。
看转移方程:
这样转移明显还是的,但是我们可以发现,其实方程就只有最大值、最小值、与有关系了。我们先前分析到是有单调性的,所以在最大值与最小值确定的情况下,越大,答案越优。
所以,我们就只需要考虑最大值与最小值变化的状态了。
我们可以用单调栈预处理出每一个位置后面第一个大于/小于它的数的位置,转移的时候直接跳就好
时间复杂度……有些玄学,不过跑的挺快的
代码
/********************** User:fxyly Language:c++ Problem:Nowcoder Algorithm: **********************/ #include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; int n,w,tp; long long a[maxn], f[maxn], sum[maxn]; int suc_ma[maxn], suc_mi[maxn], s[maxn]; template<class T>inline void read(T &x){ x = 0;char ch = getchar();bool flag = 0; while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); if(flag) x = -x; } template<class T>void putch(const T x){if(x > 9) putch(x / 10);putchar(x % 10 | 48);} template<class T>void put(const T x){if(x < 0) putchar('-'),putch(-x);else putch(x);} void readdata(){ read(n);read(w); for(int i = 1; i <= n; ++ i) read(a[i]), sum[i] = sum[i - 1] + a[i]; } void work(){ //单调栈维护i后第一个大于/小于i的位置 //时刻记得s中是位置 tp = 0; for(int i = n; i >= 1; -- i){//get suc_mi while(tp && a[i] <= a[s[tp - 1]]) -- tp; if(! tp) suc_mi[i] = n + 1; else suc_mi[i] = s[tp - 1]; s[tp ++ ] = i; } tp = 0; for(int i = n; i >= 1; -- i){//get suc_ma while(tp && a[i] >= a[s[tp - 1]]) -- tp; if(! tp) suc_ma[i] = n + 1; else suc_ma[i] = s[tp - 1]; s[tp ++ ] = i; } //倒序,解决了第i个箱子的费用为sum[l,r]*i的问题 for(int i = n;i >= 1; -- i){ //找到这个位置最多能装到的位置 int pos = upper_bound(sum + 1, sum + n + 1, sum[i - 1] + w) - (sum + 1); f[i] = f[i + 1] + sum[n] - sum[i - 1]; int mipos = i, mapos = i, j = i; long long cur_sum = sum[n] - sum[i - 1]; while(j <= pos){ j = min(pos, min(suc_mi[mipos] - 1, suc_ma[mapos] - 1)); //f数组从后往前递增(物品增多了嘛) ,所以转移的位置越后越好(最大最小不变的情况下) f[i] = min(f[i], f[j + 1] + cur_sum + a[mapos] - a[mipos]); j ++; if(a[j] < a[mipos]) mipos = j; if(a[j] > a[mapos]) mapos = j; } } put(f[1]); } int main(){ readdata(); work(); return 0; }
T3 地形计算
题目传送门:地形计算
题目描述
分析
我个人认为出题人的讲解非常好我就不说了
一句话,暴搜,加一个就可以了
代码
/********************* User:fxyly Language:c++ Problem:Nowcoder Algorithm: *********************/ #include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; const int mod = 1e9 + 7; int n, m, size,tp,now; int first[maxn],s[maxn]; long long a[maxn],ans,tot[maxn]; long long vis[maxn],used[maxn]; struct Edge{ int v, nt; }edge[maxn << 1]; struct Degree{ int d, id; }ind[maxn]; template<class T>inline void read(T &x){ x = 0;bool flag = 0;char ch = getchar(); while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); if(flag) x = -x; } template<class T>void putch(const T x) {if(x > 9) putch(x / 10);putchar(x % 10 | 48);} template<class T>void put(const T x) {if(x < 0) putchar('-'),putch(-x);else putch(x);} void eadd(int u, int v){ edge[++size].v = v; edge[size].nt = first[u]; first[u] = size; } void readdata(){ read(n); read(m); for(int i = 1; i <= n; ++ i) read(a[i]), ind[i].id = i; for(int i = 1; i <= m; ++ i) { int u, v;read(u); read(v); eadd(u, v); eadd(v, u); ind[u].d ++; ind[v].d ++; } } bool cmp(const Degree &x,const Degree &y){ return x.d > y.d; } void dfs(int u,int f,int d,long long sum){ if(d == 2) { if(vis[u]) ans = (ans + vis[u] + a[f] * tot[u] % mod) % mod; //一个点可能有很多条路径,所以是累加 vis[u] += sum;tot[u] ++; return; } for(int i = first[u];i;i = edge[i].nt){ int v = edge[i].v;if(v == f) continue; //因为只扩展两层,而且没有重边,只需要标记深度为两层的点,判断一下反向边就好, //如果不是两层,就需要另存数组来看这个点是否在栈中 if(used[v]) continue;//已经删除的点 dfs(v,u,d + 1,(sum + a[v]) % mod); } } void del(int u,int f,int d){ if(d == 2) {vis[u] = tot[u] = 0;return;} for(int i = first[u];i;i = edge[i].nt){ int v = edge[i].v;if(v == f) continue; if(used[v]) continue; del(v,u,d + 1); } } void work(){ sort(ind + 1,ind + 1 + n,cmp); for(int i = 1;i <= n; ++ i){ now = ind[i].id; dfs(ind[i].id,0,0,a[ind[i].id]); del(ind[i].id,0,0);used[ind[i].id] = 1; } put(ans); } int main(){ readdata(); work(); return 0; }