这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
2020-2021:teams:legal_string:jxm2001:contest:2020牛客国庆集训派对 [2020/10/04 19:43] jxm2001 [B、Arithmetic Progressions] |
2020-2021:teams:legal_string:jxm2001:contest:2020牛客国庆集训派对 [2020/10/07 21:26] (当前版本) jxm2001 [C、Expect to wait] |
||
---|---|---|---|
行 113: | 行 113: | ||
} | } | ||
enter(ans); | enter(ans); | ||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | ==== H、Colorful Tree ==== | ||
+ | |||
+ | === 题意 === | ||
+ | |||
+ | 给定一棵树,树上每点有一种颜色。接下来两种操作: | ||
+ | |||
+ | - 询问包含所有颜色为 $c$ 的结点的最小子图的边数 | ||
+ | - 将某点的颜色修改为 $c$ | ||
+ | |||
+ | === 题解 === | ||
+ | |||
+ | 将无根树转为以 $1$ 为根的有根树,同时处理得到每个点的 $\text{dfs}$ 序。 | ||
+ | |||
+ | 不难发现,对颜色为 $c$ 的结点的最小子图的边数等价于所有该颜色结点所有 $\text{dfs}$ 序相邻结点距离加上 $\text{dfs}$ 序最大最小两点距离和除以 $2$。 | ||
+ | |||
+ | 考虑 $\text{set}$ 维护即可,时间复杂度 $O((n+q)\log n)$。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | const int MAXN=1e5+5; | ||
+ | struct Edge{ | ||
+ | int to,next; | ||
+ | }edge[MAXN<<1]; | ||
+ | int head[MAXN],edge_cnt; | ||
+ | void Insert(int u,int v){ | ||
+ | edge[++edge_cnt]=Edge{v,head[u]}; | ||
+ | head[u]=edge_cnt; | ||
+ | } | ||
+ | namespace LCA{ | ||
+ | int d[MAXN],sz[MAXN],f[MAXN],dfn[MAXN],node_id[MAXN],dfs_t; | ||
+ | int h_son[MAXN],mson[MAXN],p[MAXN]; | ||
+ | void dfs_1(int u,int fa,int depth){ | ||
+ | dfn[u]=++dfs_t;sz[u]=1;f[u]=fa;d[u]=depth;mson[u]=0;node_id[dfs_t]=u; | ||
+ | for(int i=head[u];i;i=edge[i].next){ | ||
+ | int v=edge[i].to; | ||
+ | if(v==fa) | ||
+ | continue; | ||
+ | dfs_1(v,u,depth+1); | ||
+ | sz[u]+=sz[v]; | ||
+ | if(sz[v]>mson[u]) | ||
+ | h_son[u]=v,mson[u]=sz[v]; | ||
+ | } | ||
+ | } | ||
+ | void dfs_2(int u,int top){ | ||
+ | p[u]=top; | ||
+ | if(mson[u])dfs_2(h_son[u],top); | ||
+ | for(int i=head[u];i;i=edge[i].next){ | ||
+ | int v=edge[i].to; | ||
+ | if(v==f[u]||v==h_son[u]) | ||
+ | continue; | ||
+ | dfs_2(v,v); | ||
+ | } | ||
+ | } | ||
+ | void init(int root){dfs_1(root,0,0);dfs_2(root,root);} | ||
+ | int query_lca(int u,int v){ | ||
+ | while(p[u]!=p[v]){ | ||
+ | if(d[p[u]]<d[p[v]])swap(u,v); | ||
+ | u=f[p[u]]; | ||
+ | } | ||
+ | return d[u]<d[v]?u:v; | ||
+ | } | ||
+ | int query_dis(int u,int v){ | ||
+ | u=node_id[u],v=node_id[v]; | ||
+ | return d[u]+d[v]-2*d[query_lca(u,v)]; | ||
+ | } | ||
+ | }; | ||
+ | typedef set<int>::iterator iter; | ||
+ | set<int>s[MAXN]; | ||
+ | int c[MAXN]; | ||
+ | LL ans[MAXN]; | ||
+ | void del(int c,int u){ | ||
+ | iter it=s[c].find(u); | ||
+ | int l=0,r=0; | ||
+ | if(++it!=s[c].end()) | ||
+ | r=*it; | ||
+ | if(--it!=s[c].begin()) | ||
+ | l=*(--it); | ||
+ | if(l&&r)ans[c]+=LCA::query_dis(l,r); | ||
+ | if(l)ans[c]-=LCA::query_dis(l,u); | ||
+ | if(r)ans[c]-=LCA::query_dis(u,r); | ||
+ | s[c].erase(u); | ||
+ | } | ||
+ | void add(int c,int u){ | ||
+ | s[c].insert(u); | ||
+ | iter it=s[c].find(u); | ||
+ | int l=0,r=0; | ||
+ | if(++it!=s[c].end()) | ||
+ | r=*it; | ||
+ | if(--it!=s[c].begin()) | ||
+ | l=*(--it); | ||
+ | if(l&&r)ans[c]-=LCA::query_dis(l,r); | ||
+ | if(l)ans[c]+=LCA::query_dis(l,u); | ||
+ | if(r)ans[c]+=LCA::query_dis(u,r); | ||
+ | } | ||
+ | int main() | ||
+ | { | ||
+ | int n=read_int(); | ||
+ | _for(i,1,n){ | ||
+ | int u=read_int(),v=read_int(); | ||
+ | Insert(u,v);Insert(v,u); | ||
+ | } | ||
+ | LCA::init(1); | ||
+ | _rep(i,1,n){ | ||
+ | c[i]=read_int(); | ||
+ | add(c[i],LCA::dfn[i]); | ||
+ | } | ||
+ | int q=read_int(); | ||
+ | while(q--){ | ||
+ | char opt=get_char(); | ||
+ | if(opt=='Q'){ | ||
+ | int v=read_int(); | ||
+ | if(s[v].empty()) | ||
+ | enter(-1); | ||
+ | else | ||
+ | enter((ans[v]+LCA::query_dis(*s[v].begin(),*(--s[v].end())))/2); | ||
+ | } | ||
+ | else{ | ||
+ | int u=read_int(),v=read_int(); | ||
+ | del(c[u],LCA::dfn[u]); | ||
+ | add(v,LCA::dfn[u]); | ||
+ | c[u]=v; | ||
+ | } | ||
+ | } | ||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | ===== Day 7 ===== | ||
+ | |||
+ | [[https://ac.nowcoder.com/acm/contest/7858|比赛链接]] | ||
+ | |||
+ | ==== C、Expect to wait ==== | ||
+ | |||
+ | === 题意 === | ||
+ | |||
+ | 给定 $n$ 个事件,时间分两种: | ||
+ | |||
+ | - $t$ 时刻有 $k$ 人借车 | ||
+ | - $t$ 时刻新加入 $k$ 辆车 | ||
+ | |||
+ | 接下来 $q$ 个询问,每次询问当初始时有 $b$ 辆车时所有人的等待时间和。 | ||
+ | |||
+ | === 题解 === | ||
+ | |||
+ | 先假设初始时没有车,考虑根据初始事件构造“时间 $-$ 等待人数”图,则答案恰好为该图形围成的面积。 | ||
+ | |||
+ | 考虑初始时有 $b$ 辆车的情况,易知这等价于将图形向下移动 $b$ 个单位,同时删去小于 $0$ 的部分。 | ||
+ | |||
+ | 不难发现这同时等价于直线 $y=b$ 与原图形上方曲线围成面积。考虑将询问根据 $b$ 排序,利用扫描线法维护答案,时间复杂度 $O(n\log n)$。 | ||
+ | |||
+ | 需要注意无解情况的特判。另外发现也可以吉司机线段树区间置 $\max$ 再询问 $\text{sum}$ 无脑维护。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | const int MAXN=1e5+5; | ||
+ | struct Seg{ | ||
+ | int len,v; | ||
+ | bool operator < (const Seg &b)const{ | ||
+ | return v>b.v; | ||
+ | } | ||
+ | }seg[MAXN]; | ||
+ | struct Query{ | ||
+ | int v,idx; | ||
+ | bool operator < (const Query &b)const{ | ||
+ | return v>b.v; | ||
+ | } | ||
+ | }que[MAXN]; | ||
+ | LL ans[MAXN]; | ||
+ | int tim[MAXN],k[MAXN]; | ||
+ | int main() | ||
+ | { | ||
+ | int n=read_int(),q=read_int(); | ||
+ | _rep(i,1,n){ | ||
+ | char c=get_char(); | ||
+ | tim[i]=read_int(),k[i]=read_int(); | ||
+ | if(c=='+')k[i]=-k[i]; | ||
+ | } | ||
+ | int st=0; | ||
+ | _for(i,1,n){ | ||
+ | seg[i].len=tim[i+1]-tim[i]; | ||
+ | seg[i].v=st+=k[i]; | ||
+ | } | ||
+ | st+=k[n]; | ||
+ | st=max(0,st); | ||
+ | sort(seg+1,seg+n); | ||
+ | _for(i,0,q){ | ||
+ | que[i].v=read_int(); | ||
+ | que[i].idx=i; | ||
+ | } | ||
+ | sort(que,que+q); | ||
+ | int pos=1,slen=0; | ||
+ | LL sum=0; | ||
+ | _for(i,0,q){ | ||
+ | if(que[i].v<st){ | ||
+ | ans[que[i].idx]=-1; | ||
+ | continue; | ||
+ | } | ||
+ | if(i)sum+=1LL*(que[i-1].v-que[i].v)*slen; | ||
+ | while(pos<n&&que[i].v<seg[pos].v){ | ||
+ | slen+=seg[pos].len; | ||
+ | sum+=1LL*(seg[pos].v-que[i].v)*seg[pos].len; | ||
+ | pos++; | ||
+ | } | ||
+ | ans[que[i].idx]=sum; | ||
+ | } | ||
+ | _for(i,0,q){ | ||
+ | if(~ans[i]) | ||
+ | enter(ans[i]); | ||
+ | else | ||
+ | puts("INFINITY"); | ||
+ | } | ||
return 0; | return 0; | ||
} | } | ||
</code> | </code> | ||
</hidden> | </hidden> |