一、题意
给出N个方块,要求给出一个方案,使得
1、 所有方块都被使用到(题目数据保证这点)
2、所有方块垒成一个塔,且上面的方块宽度小于下面的方块
3、每个方块只能用一次,可以横着或者竖着。
n范围50w
二、题解
首先考虑表示一个正方形的方法:长度和宽度组成的无向图。
因为必然要把所有方块都堆好,所以仅考虑冲突情况:即一个边长出现了多次(如果仅仅出现了一次就无须考虑,直接无缝的塞进适应的位置即可)。
因而仅仅需要考虑连成片的图(联通块情况)
对于一个图,都会有如下设定:
1、每条边最多只能做一次“宽”,因而如果出现了多条边指向这个点的话,将会必然增加deg-1次高(因为每个边长作为宽度最多只能出现一次)
2、对于树状情况,若果有X个长宽组成一个联通块,则必然至少包括X-1个边,讨论上条定理,必然会有一个边应当作为高出现,因而建议选择最大的。
三、代码
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<math.h> #include<algorithm> #include<vector> #include<map> #include<set> #include<string.h> #include<queue> using namespace std; #define ll long long const ll MAXN=500233; map<int,int> mapp; ll n,val[MAXN],vis[MAXN]; vector<int>G[MAXN]; ll deg,maxx; ll ans; void dfs(int now) { if(vis[now])return; vis[now]=1; ans+=val[now]*(G[now].size()-1); maxx=max(maxx,val[now]); deg+=G[now].size()-2; for(int i=0;i<G[now].size();++i) { dfs(G[now][i]); } } int main() { while(cin>>n) { mapp.clear(); int id=0; for(int i=1;i<=MAXN;++i)G[i].clear(); for(int i=1;i<=n;++i) { int a,b,aa,bb; scanf("%d%d",&a,&b); if(mapp.count(a))aa=mapp[a]; else aa=mapp[a]=id++; if(mapp.count(b))bb=mapp[b]; else bb=mapp[b]=id++; val[aa]=a; val[bb]=b; G[aa].push_back(bb); G[bb].push_back(aa); } memset(vis,0,sizeof(vis)); ans=0; for(int i=0;i<id;++i) { if(!vis[i]) { deg=0; maxx=0; dfs(i); if(deg<0)ans+=maxx; } } cout<<ans<<endl; } return 0; }