本次比赛题解:戳这里 ——> 题解
写在前面
迟到的总结。
不过个人觉得这一次题出的很好(* ̄︶ ̄)。
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;
} 
京公网安备 11010502036488号