zoukankan      html  css  js  c++  java
  • [ZOJ3989]Triangulation

    CXXXV.[ZOJ3989]Triangulation

    神题。

    这个数据范围很难不让人想到状压DP。于是我们考虑应该怎么设计状态。

    考虑一组三角剖分的形态:其必定是在所有点所构成的凸包内部划分出很多三角形。这也就表明,任何一组三角剖分一定包含所有凸包上的边。

    我们可以想到一个比较简洁的DP:设 \(f(\mathbb S)\) 表示在点集 \(\mathbb S\) 所构成的凸包内部的所有点的三角剖分。但是这样子跑的话,每一组三角剖分都会被统计多次,不能不重复地计数。

    于是我们另辟蹊径。我们令初始三角剖分上的边仅包含所有点集的下凸壳上的所有边。之后,维护当前剖分的上边界,每次往上边界上添加一个新的三角形,得到新的上边界,这样持续不断直到填成了整个点集的上凸壳

    但是,出于不重复计数的考虑,我们必须对该上边界做出限制。

    我们强制该上边界上的点的 \(x\) 坐标严格递增。这样,只需知道点集就能唯一还原出边界,而不必存储点集内部的顺序了。

    同时,为了处理有点 \(x\) 坐标相同的情形,我们将所有点随机旋转一个角度,这样便消除了 \(x\) 坐标相同的可能(如果仍相同,继续转即可)。于是以下默认所有点 \(x\) 坐标互不相同。

    接着考虑如何添加三角形。

    我们发现,其可以分为两种情形:

    1. 对于边界上连续的三个点 \(A,B,C\),满足 \(B\)\(AC\) 下方,且 \(\triangle_{ABC}\) 内部无点,此时便可以连边 \(AC\),将 \(B\) 从边界上删除。

    2. 对于边界上连续的两个点 \(A,B\),存在一个 \(C\)\(AB\) 上方,且 \(C\)\(x\) 坐标介于 \(A,B\) 之间,且 \(\triangle_{ABC}\) 内部无点,此时便可以连边 \(AC,BC\),并将 \(C\) 插入上边界。

    为了不重复计算,我们每次仅加入上边界上最左的三角形。也就是说,如果一条边 \(AB\) 在某一轮转移中没有生长出三角形来,则其之后也一定不会再长三角形。同时,对于情形1,我们将其算作右边的 \(BC\) 边长出的三角形,这样便统一了条件。

    这样,我们便可以设 \(f(\mathbb S,i)\) 表示现在上边界是集合 \(\mathbb S\),且 \(\mathbb S\) 中前 \(i\) 条边都已经无法再生长的状态。这时,考虑第 \(i+1\) 条边,处理其长或不长的状态即可。

    需要注意的是,因为 \(x\) 坐标最大最小的两个点无论何时都必在上边界上,所以可以在状态中不记录它们,这样剩下的所有状态都是可能出现的状态了。且因为没有明确的转移顺序,采取bfs转移。

    通过预处理一大坨东西(三角形内部有没有点啦、情形2的哪些 \(C\) 合法啦,之类的),我们可以做到 \(O(n^4+n2^n)\) 的复杂度,其中 \(n^4\) 是预处理。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int T,n,ord[20],a[20][20],rev[20],f[1<<16][17],in[1<<16],b[20];
    ll g[1<<16][17];
    const double eps=1e-13;
    const double pi=acos(-1);
    int cmp(double x){
    	if(x>eps)return 1;
    	if(x<-eps)return -1;
    	return 0;
    }
    struct Vector{
    	double x,y;
    	Vector(){}
    	Vector(double X,double Y){x=X,y=Y;}
    	friend Vector operator +(const Vector &u,const Vector &v){return Vector(u.x+v.x,u.y+v.y);}
    	friend Vector operator -(const Vector &u,const Vector &v){return Vector(u.x-v.x,u.y-v.y);}
    	friend Vector operator *(const Vector &u,const double &v){return Vector(u.x*v,u.y*v);}
    	friend Vector operator /(const Vector &u,const double &v){return Vector(u.x/v,u.y/v);}
    	friend double operator &(const Vector &u,const Vector &v){return u.x*v.y-u.y*v.x;}//cross times
    	friend double operator |(const Vector &u,const Vector &v){return u.x*v.x+u.y*v.y;}//point times
    	friend bool operator <(const Vector &u,const Vector &v){return u.x<v.x;}
    	double operator ~()const{return sqrt(x*x+y*y);}//the modulo of a vector
    	double operator !()const{return atan2(y,x);}//the angle of a vector
    	void print(){printf("(%lf,%lf)",x,y);}
    	void rotate(double ang){
    		double modu=~*this;
    		double angl=!*this;
    		angl+=ang;
    		x=cos(angl)*modu,y=sin(angl)*modu;
    	}
    }p[20];
    typedef Vector Point;
    struct Line{
    	Point x,y;
    	Vector z;
    	Line(){}
    	Line(Point X,Point Y){x=X,y=Y,z=Y-X;}
    	friend Point operator &(const Line &u,const Line &v){return u.x+u.z*((v.z&(u.x-v.x))/(u.z&v.z));}
    };
    typedef Line Segment;
    bool CMP(int u,int v){return p[u].x<p[v].x;}
    bool lower[20],upper[20];
    int stk[20],tp,LO,UP;
    queue<int>q;
    void trans(int i,int j,int I,int J,int k){
    	if(!--in[I])q.push(I);
    	if(f[I][J]>k)f[I][J]=k,g[I][J]=g[i][j];
    	else if(f[I][J]==k)g[I][J]+=g[i][j];
    }
    vector<int>v[20][20];
    bool abv[20][20][20],ins[20][20][20];//if above, true.
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d",&n),LO=UP=0,memset(f,0x3f,sizeof(f)),memset(in,0,sizeof(in)),memset(ins,true,sizeof(ins));
    		for(int i=0;i<n;i++)scanf("%lf%lf",&p[i].x,&p[i].y),ord[i]=i,lower[i]=upper[i]=false;
    		while(true){
    			double ang=(1.0*rand()/RAND_MAX)*2*pi;
    			for(int i=0;i<n;i++)p[i].rotate(ang);
    			sort(ord,ord+n,CMP);
    			bool ok=true;
    			for(int i=1;i<n;i++)if(!cmp(p[i].x-p[i-1].x)){ok=false;break;}
    			if(ok)break;
    		}
    		for(int i=0;i<n;i++)rev[ord[i]]=i;
    		for(int i=0;i<n;i++)for(int j=0;j<n;j++)scanf("%d",&a[rev[i]][rev[j]]);
    		sort(p,p+n);
    //		for(int i=0;i<n;i++)p[i].print();puts("");
    		
    		for(int i=0;i<n;i++)for(int j=i+1;j<n;j++)for(int k=i+1;k<j;k++)abv[i][j][k]=(cmp((p[i]-p[k])&(p[j]-p[k]))==1);
    		
    		stk[++tp]=0;
    		for(int i=1;i<n;i++){
    			while(tp>=2&&cmp((p[stk[tp-1]]-p[stk[tp]])&(p[i]-p[stk[tp]]))!=-1)tp--;
    			stk[++tp]=i;
    		}
    		for(int i=1;i<=tp;i++)lower[stk[i]]=true,LO|=1<<stk[i];
    		LO=(LO^(1<<(n-1)))>>1,f[LO][0]=0,g[LO][0]=1;
    		for(int i=1;i<tp;i++)f[LO][0]+=a[stk[i]][stk[i+1]];
    		tp=0;
    		
    		stk[++tp]=0;
    		for(int i=1;i<n;i++){
    			while(tp>=2&&cmp((p[stk[tp-1]]-p[stk[tp]])&(p[i]-p[stk[tp]]))!=1)tp--;
    			stk[++tp]=i;
    		}
    		for(int i=1;i<=tp;i++)upper[stk[i]]=true,UP|=1<<stk[i];
    		UP=(UP^(1<<(n-1)))>>1;
    		tp=0;
    		
    //		printf("STA:%d %d\n",LO,UP);
    //		for(int i=0;i<n;i++)printf("(%d %d)",lower[i],upper[i]);puts("");
    		
    		for(int i=0;i<n;i++)for(int j=i+1;j<n;j++){
    			for(int k=i+1;k<j;k++){
    				if(abv[i][j][k]){
    					bool ok=true;
    					for(int l=i+1;l<k;l++)if(abv[i][j][l]&&!abv[i][k][l]){ok=false;break;}
    					if(!ok)continue;
    					for(int l=k+1;l<j;l++)if(abv[i][j][l]&&!abv[k][j][l]){ok=false;break;}
    					if(!ok)continue;
    					v[i][j].push_back(k);					
    				}else{
    					ins[i][j][k]=false;
    					for(int l=i+1;l<k;l++)if(!abv[i][j][l]&&abv[i][k][l]){ins[i][j][k]=true;break;}
    					for(int l=k+1;l<j;l++)if(!abv[i][j][l]&&abv[k][j][l]){ins[i][j][k]=true;break;}
    				}
    			}
    		}
    		
    		for(int i=0;i<(1<<(n-2));i++){
    			int len=0;b[len++]=0;
    			for(int j=0;j<n-2;j++)if(i&(1<<j))b[len++]=j+1;
    			b[len++]=n-1;
    //			printf("%d:",i);for(int j=0;j<len;j++)printf("%d ",b[j]);puts("");
    			for(int j=0;j<len;j++){
    				if(j&&j+1<len&&!ins[b[j-1]][b[j+1]][b[j]])in[i^(1<<(b[j]-1))]++;
    				if(j)for(auto k:v[b[j-1]][b[j]])in[i|(1<<(k-1))]++;
    			}
    		}
    		q.push(LO);
    		while(!q.empty()){
    			int i=q.front();q.pop();
    //			printf("SS:%d\n",i);
    			int len=0;b[len++]=0;
    			for(int j=0;j<n-2;j++)if(i&(1<<j))b[len++]=j+1;
    			b[len++]=n-1;
    			for(int j=0;j<len;j++){
    				if(j&&j+1<len&&!ins[b[j-1]][b[j+1]][b[j]])trans(i,j,i^(1<<(b[j]-1)),j-1,f[i][j]+a[b[j-1]][b[j+1]]);
    				if(j)for(auto k:v[b[j-1]][b[j]])trans(i,j-1,i|(1<<(k-1)),j-1,f[i][j-1]+a[b[j-1]][k]+a[b[j]][k]);
    				if(j+2<len)trans(i,j,i,j+1,f[i][j]);
    			}
    		}
    		printf("%d %lld\n",f[UP][__builtin_popcount(UP)],g[UP][__builtin_popcount(UP)]);
    		
    		for(int i=0;i<n;i++)for(int j=i+1;j<n;j++)v[i][j].clear();
    	}
    	return 0;
    }
    

  • 相关阅读:
    反序列化
    反序列化使用
    Serializer序列器
    DRF工程搭建
    JDK目录介绍
    Java环境变量配置
    Java语言的特性
    Java语言概述
    计算机编程语言介绍
    软件开发介绍
  • 原文地址:https://www.cnblogs.com/Troverld/p/14601549.html
Copyright © 2011-2022 走看看