====== 2020牛客国庆集训派对 ======
===== Day 1 =====
[[https://ac.nowcoder.com/acm/contest/7817|比赛链接]]
==== I、Saba1000kg ====
=== 题意 ===
给定一张图,$q$ 个询问,每次询问只考虑图中 $s_i$ 个点构成的连通块数。数据保证 $\sum s_i\le 10^5$。
=== 题解 ===
考虑分块。当 $s_i\lt k$ 时暴力 $O(s_i^2)$ 枚举所有点对同时维护并查集。 $s_i\ge k$ 时暴力 $O(m)$ 枚举所有边同时维护并查集。
从均摊复杂度考虑,消耗每点 $\sum s_i$ 的复杂度为 $O(\max(\cfrac {s_i^2}{s_i}(s_i\lt k),\cfrac m{s_i}(s_i\ge k))\log s_i)=O(\max (k,\cfrac mk)\log k)$。
取 $k=O(\sqrt m)$ 时总时间复杂度为 $O(\sum s_i\sqrt m\log m)$。
const int MAXN=1e5+5;
setg[MAXN];
struct Edge{
int u,v;
}edge[MAXN];
int p[MAXN],b[MAXN];
bool vis[MAXN];
int Find(int x){return x==p[x]?x:p[x]=Find(p[x]);}
void Merge(int x,int y){
int xx=Find(x),yy=Find(y);
if(xx!=yy)
p[xx]=yy;
}
int main()
{
int n=read_int(),m=read_int(),q=read_int(),limt=sqrt(m+10);
_for(i,0,m){
edge[i].u=read_int(),edge[i].v=read_int();
g[edge[i].u].insert(edge[i].v);
g[edge[i].v].insert(edge[i].u);
}
while(q--){
int sz=read_int(),ans=0;
_for(i,0,sz){
b[i]=read_int();
vis[b[i]]=true;
p[b[i]]=b[i];
}
if(sz
===== Day 4 =====
[[https://ac.nowcoder.com/acm/contest/7831|比赛链接]]
==== B、Arithmetic Progressions ====
=== 题意 ===
给定一个不可重集,求最大子集满足子集中数构成等差数列。
=== 题解 ===
先将不可重集排序得到序列 $A$,然后令 $\text{dp}(i,j)$ 表示等差数列最后一项为 $a_j$ 且倒数第二项为 $a_i$ 时的等差数列的长度。
考虑枚举 $i$,同时双指针 $k\lt i\lt j$ 查找满足 $a_k+a_j=a_i$ 的数,有状态转移 $\text{dp}(i,j)=\text{dp}(k,i)+1$。时间复杂度 $O(n^2)$。
const int MAXN=5e3+5;
int a[MAXN],dp[MAXN][MAXN];
int main()
{
int n=read_int();
_for(i,0,n)a[i]=read_int();
sort(a,a+n);
_for(i,0,n)_for(j,i+1,n)dp[i][j]=2;
int ans=2;
_for(i,0,n){
int pos1=i-1,pos2=i+1;
while(pos1>=0&&pos2(a[i]<<1))pos1--;
else{
dp[i][pos2]=max(dp[i][pos2],dp[pos1][i]+1);
ans=max(ans,dp[i][pos2]);
pos1--;pos2++;
}
}
}
enter(ans);
return 0;
}
==== H、Colorful Tree ====
=== 题意 ===
给定一棵树,树上每点有一种颜色。接下来两种操作:
- 询问包含所有颜色为 $c$ 的结点的最小子图的边数
- 将某点的颜色修改为 $c$
=== 题解 ===
将无根树转为以 $1$ 为根的有根树,同时处理得到每个点的 $\text{dfs}$ 序。
不难发现,对颜色为 $c$ 的结点的最小子图的边数等价于所有该颜色结点所有 $\text{dfs}$ 序相邻结点距离加上 $\text{dfs}$ 序最大最小两点距离和除以 $2$。
考虑 $\text{set}$ 维护即可,时间复杂度 $O((n+q)\log n)$。
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]]::iterator iter;
sets[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;
}
===== 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}$ 无脑维护。
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