HGOI20191115 模拟赛 题解
Problem A 表演
有$n$个有点权的点,$m$个有边权的边。对于每个点$u$,输出从这个点出发到$v$,其路径权值的两倍加上v的点权和最小的值。
对于$100\%$的数据,满足$1 \leq n,m \leq 2\times 10^5 $
Solution :
考虑一个简单的转移,$f[u]$表示点$u$的最小答案,最初$f[u]$ 为$u$的点权。
每一次更新,就是相邻的两个点$u,v$之间,用边权的两倍来更新答案。
如果在图上DP,那么就相当于,将初始这些点权加入priority_queue,跑最短路即可。
时间复杂度为$O(m log_2 n)$
# include <bits/stdc++.h> # define int long long using namespace std; const int N=2e5+10; struct rec{ int pre,to,w;}a[N<<1]; int n,m,tot; bool inq[N]; int head[N],d[N],val[N]; namespace Fast_IO { inline int read() { int x=0,w=0; char c=0; while (c<'0'||c>'9') w|=c=='-',c=getchar(); while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return w?-x:x; } void write(int x) { if (x<0) x=-x,putchar('-'); if (x>9) write(x/10); putchar('0'+x%10); } void writeln(int x) { write(x); putchar('\n'); } }; using namespace Fast_IO; void adde(int u,int v,int w) { a[++tot].pre=head[u]; a[tot].to=v; a[tot].w=w*2; head[u]=tot; } struct Node { int id,val; }; struct cmp { bool operator () (Node a,Node b) { return a.val > b.val; } }; priority_queue<Node,vector<Node>,cmp>q; void dijkstra() { for (int i=1;i<=n;i++) { d[i]=val[i]; inq[i]=true; q.push((Node){i,val[i]}); } while (q.size()) { int u=q.top().id; q.pop(); inq[u]=false; for (int i=head[u];i;i=a[i].pre) { int v=a[i].to; if (d[v]>d[u]+a[i].w) { d[v]=d[u]+a[i].w; if (!inq[v]) q.push((Node){v,d[v]}); } } } } signed main() { n=read();m=read(); for (int i=1;i<=m;i++) { int u=read(),v=read(),w=read(); adde(u,v,w); adde(v,u,w); } for (int i=1;i<=n;i++) val[i]=read(); dijkstra(); for (int i=1;i<n;i++) write(d[i]),putchar(' '); writeln(d[n]); return 0; }
Problem B 逮虾户
要求的车速是一个大于等于0的值,设第$i$次估计的车速为$v_i$,走的路程为$s_i$,
设$v'$表示真实速度,则有偏差值$d$的定义式为 $d = v' - v_i$。
考虑对速度的估计的偏差值d每一次都恒定。
给出$n$次测算的结果$s_i , v_i$,和这几次测试总共的用时$t$,输出$d$的值。
对于$100\%$的数据满足$1 \leq n\leq 10^3,1 \leq s_i \leq 10^3 , |v_i| \leq 10^3$
Solution :
考虑$t$这个函数是在$n+1$段不连续的定义域中单调减的。
所以,我们可以做$n+1$二分,就能找到些定义域中和d最相近的。
、 注意,最后统计答案的时候,需要check一下实际车速大于等于0.
时间复杂度为$O(n ^2 log_2 S)$,其中$S$表示确定实数域大小。
# include <bits/stdc++.h> using namespace std; const int N=1e3+10; const double eps = 1e-9; struct rec{double s,v;}a[N]; int n,t; vector<double>ans; bool cmp (rec x, rec y) { return x.v>y.v; } double f (double x) { double res = 0; for (int i=1;i<=n;i++) res += (double)a[i].s/(double)(x+a[i].v); return res; } double work(double l,double r) { while (fabs(r-l)>=eps) { double mid = (l+r)/2.0; if (f(mid)<=t) r=mid; else l=mid; } return l; } double calc(double x) { return fabs(f(x)-t); } int main() { scanf("%d%d",&n,&t); for (int i=1;i<=n;i++) scanf("%lf%lf",&a[i].s,&a[i].v); sort(a+1,a+1+n,cmp); double l=-1e9; for (int i=1;i<=n;i++) { ans.push_back(work(l,-a[i].v-eps)); l=-a[i].v+eps; } ans.push_back(work(l,1e9)); double res = ans[0], delta =calc(ans[0]); for (int i=1;i<ans.size();i++) { bool ok = true; for (int j=1;j<=n;j++) if (a[j].v+ans[i]<eps) { ok = false; break; } if (!ok) continue; double tmp = calc(ans[i]); if (tmp <= delta) { res = ans[i]; delta = tmp; } } printf("%.10lf\n",res); return 0; }
Problem C 跳一跳
如果序列中任意相邻两个元素,都是极值点,其中一个是极大值点,另外一个极小值点。
那么这个序列叫做波浪序列。
给出对于长度为$n$序列,输出最长波浪序列的长度。
对于$100\%$的数据满足 $1 \leq n \leq 10^5 , 1 \leq a_i \leq 10^9 $
Solution :
由于元素只进行比较,所以我们可以将其离散化。
设$f[i][0]$表示第$i$个元素作为极小点为结尾的最长波浪序列长度,$f[i][1]$表示第$i$个元素作为极大点为结尾的最长波浪序列长度。
转移方程为:$f[i][0] = max(f[j][1] + 1) (a_i < a_j) , f[i][1] = max (f[j][0] + 1) (a_i > a_j)$
可以用值域线段树来维护这个$DP$。时间复杂度为$O(n log_2 n)$
# include <bits/stdc++.h> using namespace std; const int N=1e5+10; int a[N],n,f[N][2]; vector<int>tmp; namespace Fast_IO { inline int read() { int x=0,w=0; char c=0; while (c<'0'||c>'9') w|=c=='-',c=getchar(); while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return w?-x:x; } void write(int x) { if (x<0) x=-x,putchar('-'); if (x>9) write(x/10); putchar('0'+x%10); } void writeln(int x) { write(x); putchar('\n'); } }; using namespace Fast_IO; struct Segment_Tree { int tr[N<<2]; # define ls (x<<1) # define rs (x<<1|1) # define mid (l+r>>1) # define lson ls,l,mid # define rson rs,mid+1,r void update(int x,int l,int r,int pos,int opx) { if (l==r) { tr[x]=max(tr[x],opx); return; } if (pos<=mid) update(lson,pos,opx); else update(rson,pos,opx); tr[x]=max(tr[ls],tr[rs]); } int query(int x,int l,int r,int opl,int opr) { if (opl<=l && r<=opr) return tr[x]; int ret=0; if (opl<=mid) ret=max(ret,query(lson,opl,opr)); if (opr>mid) ret=max(ret,query(rson,opl,opr)); return ret; } }tr0,tr1; int main() { n=read(); for (int i=1;i<=n;i++) { tmp.push_back(a[i]=read()); } sort(tmp.begin(),tmp.end()); tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end()); int m = tmp.size(); for (int i=1;i<=n;i++) { a[i]=lower_bound(tmp.begin(),tmp.end(),a[i])-tmp.begin()+1; } int ans = 0; for (int i=1;i<=n;i++) { f[i][0]=f[i][1]=1; if (a[i]-1>=1) f[i][1] = max(f[i][1],tr0.query(1,1,m,1,a[i]-1)+1); if (a[i]+1<=m) f[i][0] = max(f[i][0],tr1.query(1,1,m,a[i]+1,m)+1); ans = max(ans,max(f[i][0],f[i][1])); tr0.update(1,1,m,a[i],f[i][0]); tr1.update(1,1,m,a[i],f[i][1]); } writeln(ans); return 0; }