kmjp's blog

競技プログラミング参加記です

yukicoder : No.3148 Min-Cost Destruction of Parentheses

これ系なかなか思い出せない。
https://yukicoder.me/problems/no/3148

問題

2N文字の括弧列Sがある。
また、各括弧対にパラメータが設定されている。

Sから連続する"()"の対を1つ取り除く、ということを繰り返す。
そのつど、それまで削除した括弧対のパラメータの総和がコストとなってかかるとする。

全括弧対を取り除くのにかかる最小総コストを求めよ。

解法

Sを括弧対で囲った状態を考える。
括弧対の包含関係を根付き木の親子関係に置き換えるとする。
括弧列を取り除く順番を逆回しにすると、(0-originで)n回目に取り除いた括弧対に対し、n×(取り除く括弧対のパラメータ値)がコストに計上される。
また、この木構造において、親に相当する括弧列から先に取り除かなければならない。

ここで括弧対を取り除く順番を考える。
初期状態で各点がそれぞれ1頂点からなる集合だとする。
子の頂点集合のパラメータの平均値より、親の頂点集合のパラメータの平均値が大きい場合、親と子との集合を併合し、親側の平均値を下げる、ということを1頂点になるまで繰り返せばよい。

int N;
string S;
int P[202020];
ll A[101010];
ll B[101010];


class Compare {
public:
    bool operator()(const int& a,const int& b) const {
		if(A[a]*B[b]!=A[b]*B[a]) {
			return A[a]*B[b]>A[b]*B[a];
		}
		return a<b;
	};
};


template<int um> class UF {
	public:
	vector<int> par,rank,cnt,G[um];
	UF() {par=rank=vector<int>(um,0); cnt=vector<int>(um,1); for(int i=0;i<um;i++) par[i]=i;}
	void reinit(int num=um) {int i; FOR(i,num) rank[i]=0,cnt[i]=1,par[i]=i;}
	int operator[](int x) {return (par[x]==x)?(x):(par[x] = operator[](par[x]));}
	int count(int x) { return cnt[operator[](x)];}
	int operator()(int x,int y) {
		if((x=operator[](x))==(y=operator[](y))) return x;
		cnt[y]=cnt[x]=cnt[x]+cnt[y];
		if(rank[x]>rank[y]) return par[x]=y;
		rank[x]+=rank[x]==rank[y]; return par[y]=x;
	}
};
UF<101010> uf;
set<int,Compare> Q;

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>S;
	S="("+S+")";
	B[0]=1;
	FOR(i,N) {
		cin>>A[i+1];
		B[i+1]=1;
	}
	vector<int> V;
	x=0;
	FORR(c,S) {
		if(c=='(') {
			if(x) P[x]=V.back();
			V.push_back(x);
			x++;
		}
		else {
			V.pop_back();
		}
	}
	FOR(i,N) Q.insert(i+1);
	ll ret=0;
	while(Q.size()) {
		int cur=*Q.begin();
		Q.erase(cur);
		x=uf[P[cur]];
		Q.erase(x);
		ret+=B[x]*A[cur];
		A[cur]=A[x]=A[cur]+A[x];
		B[cur]=B[x]=B[cur]+B[x];
		P[cur]=P[x];
		uf(cur,x);
		if(uf[cur]!=uf[0]) Q.insert(uf[cur]);
	}
	cout<<ret<<endl;
	
}

まとめ

01Tree、類似問題も見てるのにさっと思い出せないなぁ。