UOJ621 【JOISC2021】最差记者 4
UOJ621 【JOISC2021】最差记者 4
线段树合并
从早上研究到晚上,终于干掉了这道题。
对于题目中rating关系建图。
首先,每个点的出度为\(1\),因此整张图是由多个基环内向树组成的,我们需要对于每个基环内向树计算答案,相加即可。
容易发现,在一棵基环内向树上,环上点的权值必然相等。
我们先考虑树的情况,需要满足子节点的权值\(\ge\)父节点的权值,可以发现最优方案中,父节点的权值一定是保持自己的权值不变,或者取子节点中的最小权值。然后再考虑叶子节点,显然它的权值要么保持不变,要么取\(INF=10^9\)。
因此在一棵大小为\(n\)的基环内向树中,节点权值取值范围最多只有\(n+1\)个数。我们可以进行\(dp\),维护一个子树的根节点权值等于任意在其子树内节点权值(或\(INF\))时的最小代价。考虑到从子节点推到父节点,比如两棵根节点权值分别为\(s,t(s<t)\)的子树,最终我们会将他们俩组成的答案合并到\(s\)中,这个可以在线段树合并的过程中实现。最终,对于一棵树,我们可以取出根节点的权值等于任意在其子树内的节点权值(或\(INF\))情况下的最小代价。
因此,对于一棵基环内向树,我们先处理每棵子树,然后直接枚举每个权值来计算答案。对于枚举的权值\(V\)来说,每棵子树根节点的权值必须\(\ge V\),我们可以默认根节点权值必须更改,然后对于那些根节点权值不更改的进行特判。
我们优化这一过程,从大到小枚举权值,然后对于每棵子树,不断拓展它的取值范围,这可以用优先队列进行动态维护。同样,对根节点权值不更改的情况进行特判。
然后我们就解决了这道题。
时间复杂度:\(O(n \log n)\)。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#define N 200005
#define M 10000005
#define ll long long
#define pr pair<int,ll>
#define mp make_pair
#define IT vector<int> :: iterator
#define pIT vector< pr > :: iterator
#define mIT map<int,bool> :: iterator
using namespace std;
const ll INF=1919191919191919;
int n,cc,a[N],h[N],c[N],d[N],rd[N],rt[N];
vector<int>e[N],val[N];
vector< pr >co[N];
queue<int>q;
map<int,bool>ht;
int z0,z[N];
bool vis[N];
int cnt,n1,n2;
ll ans,sz[N],g[N];
#define ls(p) tr[p].ch[0]
#define rs(p) tr[p].ch[1]
#define mn(p) tr[p].Mn
#define tag(p) tr[p].Tag
struct node
{
int ch[2];
ll Mn,Tag;
}tr[M];
void push_tag(int p,ll z)
{
if (!p)
return;
mn(p)+=z,tag(p)+=z;
mn(p)=min(mn(p),INF),tag(p)=min(tag(p),INF);
}
void push_down(int p)
{
if (tag(p))
{
push_tag(ls(p),tag(p));
push_tag(rs(p),tag(p));
tag(p)=0;
}
}
void update(int p)
{
mn(p)=min(mn(ls(p)),mn(rs(p)));
}
void modify(int &p,int l,int r,int x,int y)
{
if (!p)
p=++cnt;
if (l==r)
{
mn(p)+=y;
return;
}
push_down(p);
int mid(l+r >> 1);
if (x<=mid)
modify(ls(p),l,mid,x,y); else
modify(rs(p),mid+1,r,x,y);
update(p);
}
void modify2(int &p,int l,int r,int x,ll y)
{
if (!p)
p=++cnt;
if (l==r)
{
mn(p)=y;
return;
}
push_down(p);
int mid(l+r >> 1);
if (x<=mid)
modify2(ls(p),l,mid,x,y); else
modify2(rs(p),mid+1,r,x,y);
update(p);
}
int combine(int x,int y,int l,int r,ll xm,ll ym)
{
if (!x && y)
push_tag(y,xm);
if (x && !y)
push_tag(x,ym);
if (!x || !y)
return x|y;
if (l==r)
{
mn(x)=min(mn(x)+min(ym,mn(y)),mn(y)+min(xm,mn(x)));
return x;
}
push_down(x),push_down(y);
int mid(l+r >> 1);
ls(x)=combine(ls(x),ls(y),l,mid,min(xm,mn(rs(x))),min(ym,mn(rs(y))));
rs(x)=combine(rs(x),rs(y),mid+1,r,xm,ym);
update(x);
return x;
}
ll calc(int p,int l,int r,int x,int y)
{
if (!p)
return INF;
if (l==x && r==y)
return mn(p);
push_down(p);
int mid(l+r >> 1);
if (y<=mid)
return calc(ls(p),l,mid,x,y); else
if (x>mid)
return calc(rs(p),mid+1,r,x,y); else
return min(calc(ls(p),l,mid,x,mid),calc(rs(p),mid+1,r,mid+1,y));
}
void dfs(int p,int l,int r,int u)
{
if (!p)
return;
if (l==r)
{
co[u].push_back(mp(l,mn(p)));
return;
}
push_down(p);
int mid(l+r >> 1);
dfs(ls(p),l,mid,u),dfs(rs(p),mid+1,r,u);
}
void dfs(int u)
{
ht[h[u]]=true;
vis[u]=true;
sz[u]=c[u];
int ct(0);
for (IT it=e[u].begin();it!=e[u].end();++it)
{
int v(*it);
if (v==n1 || v==n2)
continue;
++ct;
}
if (!ct)
{
modify(rt[u],1,cc,h[u],0);
modify(rt[u],1,cc,cc,c[u]);
} else
{
for (IT it=e[u].begin();it!=e[u].end();++it)
{
int v(*it);
if (v==n1 || v==n2)
continue;
dfs(v);
sz[u]+=sz[v];
if (!rt[u])
rt[u]=rt[v]; else
rt[u]=combine(rt[u],rt[v],1,cc,INF,INF);
}
ll zt(calc(rt[u],1,cc,h[u],cc));
push_tag(rt[u],c[u]);
modify2(rt[u],1,cc,h[u],zt);
}
}
struct node2
{
pIT it;
int id;
node2 () {}
node2 (pIT It,int Id):it(It),id(Id) {}
bool operator < (const node2 &A) const
{
return it->first<A.it->first;
}
};
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d%d%d",&a[i],&h[i],&c[i]),e[a[i]].push_back(i),++rd[a[i]],d[i]=h[i];
sort(d+1,d+n+1);
cc=unique(d+1,d+n+1)-d-1;
for (int i=1;i<=n;++i)
h[i]=lower_bound(d+1,d+cc+1,h[i])-d;
++cc;
for (int i=1;i<=n;++i)
if (!rd[i])
q.push(i);
while (!q.empty())
{
int u(q.front());
vis[u]=true;
q.pop();
--rd[a[u]];
if (!rd[a[u]])
q.push(a[u]);
}
mn(0)=INF;
for (int i=1;i<=n;++i)
if (!vis[i])
{
z0=0;
z[++z0]=i;
for (int j=a[i];j!=i;j=a[j])
z[++z0]=j;
ll oc(0);
priority_queue<node2>Q;
for (int j=1;j<=z0;++j)
{
n1=(j==1)?z[z0]:z[j-1];
n2=(j==z0)?z[1]:z[j+1];
dfs(z[j]);
modify(rt[z[j]],1,cc,h[z[j]],c[z[j]]);
dfs(rt[z[j]],1,cc,j);
val[h[z[j]]].push_back(j);
g[j]=sz[z[j]];
oc+=g[j];
pIT it=co[j].end();
--it;
Q.push(node2(it,j));
}
ht[cc]=true;
ll tas(INF);
mIT mt=ht.end();
--mt;
for (;;--mt)
{
int x(mt->first);
while (!Q.empty() && (Q.top().it->first)>=x)
{
int u(Q.top().id);
pIT it(Q.top().it);
Q.pop();
oc-=g[u];
g[u]=min(g[u],it->second);
oc+=g[u];
if (it!=co[u].begin())
--it,Q.push(node2(it,u));
}
tas=min(tas,oc);
ll kt(oc);
for (IT it=val[x].begin();it!=val[x].end();++it)
{
int u(*it);
ll vt(calc(rt[z[u]],1,cc,x,x));
kt+=min(0LL,vt-c[z[u]]-g[u]);
}
val[x].clear();
tas=min(tas,kt);
if (mt==ht.begin())
break;
}
ans+=tas;
for (int j=1;j<=z0;++j)
co[j].clear();
ht.clear();
}
printf("%lld\n",ans);
return 0;
}