天天看點

Petrozavodsk Winter Training Camp 2018: Carnegie Mellon U Contest A. Mines

題目大意:

給你n個點,分别位于pi。每個點有個爆炸範圍ri和代價ci,花費ci可以引爆某個點,并且pi-ri到pi+ri範圍内的點都會被引爆。q個詢問,每次修改一個點的ci,每次輸出引爆所有店的最小代價。

題解:

學了下線段樹優化建立邊。首先對于這些爆炸關系連邊所點我們可以得到一個DAG,引爆所有點的代價就是所有入度為0的點的代價最小值之和。

那麼這題是區間操作,我們可以用線段樹優化建邊。

先對點安按照pi排序,對1到n建立線段樹,線段樹葉子向着對應的點連邊。

然後每個點的爆炸範圍對應就是一段區間[l,r]。我們将這個點向線段樹對應的線段區間連邊,這樣我們就能得到一張有向圖。

可以發現,這張有向圖上的關系即是所有關系的連邊。對其縮點,得到DAG。

然後對于每個1到n的點dfs,來删除那些全是線段樹點的強連通分量。然後我們對每個入度為0的點維護一個MULTISET,先用ans統計一開始的答案。

每次詢問就是删除插入與詢問最小值了。

附上代碼,樣例見最後注釋

#include<bits/stdc++.h>
using namespace std;
#define exa EXA
struct line
{
	int s,t;
	int next;
}a[10000001];
int head[1200001];
int edge;
inline void add(int s,int t)
{
	a[edge].next=head[s];
	head[s]=edge;
	a[edge].s=s;
	a[edge].t=t;
}
int n;
struct mine
{
	int p,r,c;
	int id;
	bool operator <(mine y) const
	{
		return p<y.p;
	}
}m[200001];
int fx[200001];
inline bool cmp(mine x,mine y)
{
	return x.id<y.id;
}
inline int findlower(int x)
{
	int l=1,r=n;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(x<=m[mid].p)
			r=mid-1;
		else
			l=mid+1;
	}
	return l;
}
inline int findupper(int x)
{
	int l=1,r=n;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(x<m[mid].p)
			r=mid-1;
		else
			l=mid+1;
	}
	return l;
}
struct tree
{
	int l,r;
	int x;
}tr[800001];
int tot;
inline void build(int p,int l,int r)
{
	tot++;
	tr[p].l=l;
	tr[p].r=r;
	tr[p].x=tot;
	if(l!=r)
	{
		int mid=(l+r)/2;
		build(p*2,l,mid);
		build(p*2+1,mid+1,r);
		edge++;
		add(tr[p].x,tr[p*2].x);
		edge++;
		add(tr[p].x,tr[p*2+1].x);
	}
	else
	{
		edge++;
		add(tr[p].x,tr[p].l);
	}
}
inline void add(int p,int l,int r,int d)
{
	if(l<=tr[p].l&&tr[p].r<=r)
	{
		edge++;
		add(d,tr[p].x);
	}
	else
	{
		int mid=(tr[p].l+tr[p].r)/2;
		if(l<=mid)
			add(p*2,l,r,d);
		if(r>mid)
			add(p*2+1,l,r,d);
	}
}
int scc,cnt;
bool v[1200001];
int s[1200001],top;
int dfn[1200001],low[1200001],belong[1200001];
int indeg[1200001],outdeg[1200001];
inline void tarjan(int d)
{
     int i,x;
     cnt++;
     dfn[d]=cnt;
     low[d]=cnt;
     top++;
     s[top]=d;
     v[d]=true;
     for(i=head[d];i!=0;i=a[i].next)
     {
     	  x=a[i].t;//無向圖記錄fa[d],x==fa[d] continue
          if(dfn[x]==0) 
          {
               tarjan(x);
               low[d]=min(low[d],low[x]);
          }
          else if(v[x]&&low[d]>dfn[x])//v在棧中,修改low[u]
               low[d]=dfn[x];
     }
     if(dfn[d]==low[d])//u為該強連通分量中周遊所成樹的根
     {
          scc++;
          x=s[top];
          top--;
          while(x!=d)
          {    
			   v[x]=false;
               belong[x]=scc;
               x=s[top];
               top--;
          }
          v[x]=false;
          belong[x]=scc;
     }
}
bool vx[1200001],vv[1200001];
inline void dfs(int d)
{
	vv[d]=true;
	vx[belong[d]]=true;
	int i;
	for(i=head[d];i!=0;i=a[i].next)
	{
		int t=a[i].t;
		if(!vv[t])
			dfs(t);
	}
}
inline void count_edge()
{
	int i;
	for(i=1;i<=n;i++)
	{
		if(!vv[i])
			dfs(i);
	}
	for(i=1;i<=edge;i++)
	{
		int d=a[i].s,t=a[i].t;
		if(!vx[belong[d]]||belong[d]==belong[t])
			continue;
		indeg[belong[t]]++;
	}
}
multiset<int> S[1200001];
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("data.out","w",stdout);
	int q;
	scanf("%d%d",&n,&q);
	int i;
	for(i=1;i<=n;i++)
	{
		scanf("%d%d%d",&m[i].p,&m[i].r,&m[i].c);
		m[i].id=i;
	}
	tot=n;
	build(1,1,n);
	sort(m+1,m+1+n);
	for(i=1;i<=n;i++)
	{
		int ll=findlower(m[i].p-m[i].r);
		int rr=findupper(m[i].p+m[i].r);
		rr--;
		add(1,ll,rr,i);
	}
	for(i=1;i<=n;i++)
		fx[m[i].id]=i;
	for(i=1;i<=tot;i++)
	{
		if(!dfn[i])
			tarjan(i);
	}
	count_edge();
	for(i=1;i<=n;i++)
		if(!indeg[belong[i]])
			S[belong[i]].insert(m[i].c);
	long long ans=0;
	multiset<int>::iterator it;
	for(i=1;i<=scc;i++)
	{
		if(!indeg[i])
		{
			it=S[i].begin();
			ans+=(long long)(*it);
		}
	}
	//printf("%lld\n",ans);
	int x,xx;
	for(i=1;i<=q;i++)
	{
		scanf("%d%d",&x,&xx);
		x=fx[x];
		if(!indeg[belong[x]])
		{
			it=S[belong[x]].begin();
			ans-=(long long)(*it);
			it=S[belong[x]].find(m[x].c);
			S[belong[x]].erase(it);
			m[x].c=xx;
			S[belong[x]].insert(m[x].c);
			it=S[belong[x]].begin();
			ans+=(long long)(*it);
		}
		printf("%lld\n",ans);
	}
	return 0;
}
/*
4 2
1 1 1
6 3 10
8 2 5
10 2 3
1 1
4 11
*/