2018 ACM-ICPC World Finals 部分题题解

题面

Problem C. Conquer the World &&loj6405 征服世界

题目大意:给定一棵树,树有边权。每个点上有\(a_i\)个士兵,且每个点最终需要\(b_i\)个士兵。
求最小代价。
\(n\) \(\leq\) \(2.5*10^5\),\(a_i\),\(b_i\) \(\leq\) \(1e9\)
题解:显然,我们可以直接用这棵树跑费用流。但\(n\)太大了。所以我们只能模拟一下费用流的过程了。
首先,为了保证所有的\(b_i\)都能选满,我们先给每个\(b_i\)加一个-INF的权值。
考虑维护两个堆,分别记录\(a\)的信息和\(b\)的信息。
DFS这棵树,设当前节点为\(u\)。将\(u\)的所有儿子的堆全部合并,每个点的初始权值是\(dis_i\)\(dis_i\)-INF,
指的是\(i\)到1号节点的距离。这个可以用左偏树或者stl里的pb_ds实现。
将子树信息上传完成后,开始尝试将它们两两配对。
配对终止的条件是两个堆顶元素匹配的权值\(\geq\) 0。
然后考虑如何像费用流那样进行反悔操作。
如果当前的一个匹配点和另外一个点匹配了,那么就要撤销之前的匹配。列出两次匹配的关系式,我们就可以分别得出计算\(a\),\(b\)反悔代价的算式。
因为一个点的\(a\)\(b\)不会同时反悔,且\(\sum b_i\)也不大,所以做法是正确的。
具体的反悔实现可以看我的代码。
时间复杂度:O(\(nlogn\))
代码:

#include
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
templateI read(D &res){
	res=0;register D g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
const ll INF=1e12+7;
struct E{
	int to,nt,w;
}e[505000];
#define T e[k].to
queueq;
struct Dat{
	ll w,f;
	Dat(ll _w=0,ll _f=0){w=_w;f=_f;}
	friend bool operator < (Dat x,Dat y){return x.w>y.w;}
}d[2000000];
int now,cnt,ch[2000000][2],dis[2000000],root[303000],rt[303000];
int n,m,X,Y,W,a[303000],b[303000],head[303000],tot,sum;
ll dep[303000],ans;
IN new_node(){
	re res;
	if(q.empty())return ++cnt;
	res=q.front();q.pop();
	ch[res][0]=ch[res][1]=dis[res]=d[res].w=d[res].f=0;
	return res;
}
I add(int x,int y,int w){e[++tot].to=y;e[tot].nt=head[x];head[x]=tot;e[tot].w=w;}
IN merge(int x,int y){
	if(!x||!y)return x+y;
	if(d[x]

Problem I. Triangles && loj6411 三角形

题目大意:给出一张毒瘤的图,求其中包含的三角形个数。
题解:
我们先考虑只求出所有下三角。上三角可以将整张图翻转然后求出。(以下将\(r\),\(c\)统称为\(n\)
首先,我们将整张图扫一遍,求出三个东西:每个点\(x\)能够向左上,右上和向右延伸多少。
我们按照由右到左,由左上到右下的顺序遍历每个点,看以它为下顶点能组成多少个三角形。
发现这时我们只需要考虑当前点能和多少个上面的横线匹配,因为我们已经求出了每个点向左上和右上延伸的距离。
考虑满足条件的直线一定是一段后缀,所以可以考虑用树状数组来维护。
我们求出了每个点向右延伸的距离,那么也就可以算出一条横线被撤销的时间。用vector记录下就行。
最后再这样枚举一下左下角没被做到的东西就行了。
代码实现一定要小心,记得每次做完要清空树状数组的元素。
时间复杂度:O(\(nmlogn\))。
代码:

#include
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
templateI read(D &res){
	res=0;register D g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
struct BIT{
	int t[101000];
	IN lbt(int x){return x&(-x);}
	I modi(int x,int w,int N){
		while(x<=N)t[x]+=w,x+=lbt(x);
	}
	IN ques(int x){
		re res=0;while(x)res+=t[x],x-=lbt(x);return res;
	}
}B;
char s[6060][12020];
int n,m,r,c;
ll ans;
int ul[6002][10020],ur[6002][10020],rd[6002][10020];
vectordel[6060];
int main(){
//	freopen("f.out","w",stdout);
	read(r);read(c);r=(r<<1)-1;c=(c<<1)-1;
	F(i,1,r){
		gets(s[i]+1);
		F(j,1,c)if(s[i][j]=='\0')s[i][j]=' ';
	}
	re len=(r+1)>>1;
	F(i,1,r){
		FOR(j,c,1){
			if(s[i][j]!='x')continue;
			if(s[i][j+1]=='-'&&s[i][j+2]=='-'&&s[i][j+3]=='-'&&s[i][j+4]=='x')rd[i][j]=rd[i][j+4]+1;else rd[i][j]=0;
			if(s[i-1][j-1]!=' '&&i>1&&j>1)ul[i][j]=ul[i-2][j-2]+1;else ul[i][j]=0;
			if(s[i-1][j+1]!=' '&&i>1&&j>1)<>1)-B.ques(((i-1)>>1)-min(ul[i][j],ur[i][j]));
			if(rd[i][j]){
				B.modi((i+1)>>1,1,len);//cout<<"A"<<((i+1)>>1)<>1,-1,len);//,cout<<"d"<<((d+1)>>1)<>1,-1,len);//,cout<<"D"<<((d+1)>>1)<>1)-B.ques(((i-1)>>1)-min(ul[i][j],ur[i][j]));
			if(rd[i][j]){
				B.modi((i+1)>>1,1,len);//cout<<"A"<<((i+1)>>1)<>1,-1,len);//,cout<<"D"<<((d+1)>>1)<>1,-1,len);//,cout<<"D"<<((d+1)>>1)<1&&j>1)ul[i][j]=ul[i-2][j-2]+1;else ul[i][j]=0;
			if(s[i-1][j+1]!=' '&&i>1&&j>1)-B.ques(((i-1)>>1)-min(ul[i][j],ur[i][j]));
			if(rd[i][j]){
				B.modi((i+1)>>1,1,len);
				if((rd[i][j]<<1)+i<=r)del[(rd[i][j]<<1)+i].emplace_back(i);
				else del[0].emplace_back(i);
			}
			for(auto d:del[i])B.modi((d+1)>>1,-1,len);//,cout<<"d"<<((d+1)>>1)<>1,-1,len);//,cout<<"D"<<((d+1)>>1)<>1)-B.ques(((i-1)>>1)-min(ul[i][j],ur[i][j]));
			if(rd[i][j]){
				B.modi((i+1)>>1,1,len);
				if((rd[i][j]<<1)+i<=r)del[(rd[i][j]<<1)+i].emplace_back(i);
				else del[0].emplace_back(i);
			}
			for(auto d:del[i])B.modi((d+1)>>1,-1,len);//,cout<<"d"<<((d+1)>>1)<>1,-1,len);//,cout<<"D"<<((d+1)>>1)<

你可能感兴趣的:(2018 ACM-ICPC World Finals 部分题题解)