2016 Multi-University Training Contest 1

A - Abandoned country

两个问题的组合,一个是求最小生成树,第二个是树形dp

//
//  main.cpp
//  multi2016.1.A
//
//  Created by New_Life on 16/7/25.
//  Copyright © 2016年 chenhuan001. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
#define N 100100

int n,m;
int bin[N];

struct node
{
    int to,next,w;
}edge[2*N];

int pre[N],cnt;

void add_edge(int u,int v,int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = pre[u];
    pre[u] = cnt++;
}

int findfa(int x)
{
    if(x==bin[x]) return x;
    return bin[x] = findfa(bin[x]);
}

struct EDGE
{
    int x,y,z;
}E[1001000];

int cmpE(EDGE e1,EDGE e2)
{
    return e1.z<e2.z;
}

long long all = 0;

int dfs(int s,int fa)
{
    int tcnt = 0;
    for(int p =pre[s];p!=-1;p=edge[p].next)
    {
        int v = edge[p].to;
        if(v == fa) continue;
        int w = edge[p].w;
        int tmp = dfs(v,s);
        all += (long long)w*tmp*(n-tmp);
        tcnt += tmp;
    }
    return tcnt+1;
}

int main() {
    int T;
    cin>>T;
    while(T--)
    {
        cnt = 0;
        memset(pre,-1,sizeof(pre));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            bin[i] = i;
        for(int i=0;i<m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            E[i].x = x; E[i].y = y; E[i].z = z;
        }
        sort(E, E+m, cmpE);
        long long ans=0;
        for(int i=0;i<m;i++)
        {
            int a = E[i].x;
            int b = E[i].y;
            int w = E[i].z;
            int fa = findfa(a);
            int fb = findfa(b);
            if(fa != fb)
            {
                bin[fa] = fb;
                ans += w;
                add_edge(a,b,w);
                add_edge(b,a,w);
            }
        }
        //然后搞一次dfs,e_e 自己写吧
        all = 0;
        dfs(1,-1);
        double ansp = (double)all/((double)n*(n-1)/2);
        printf("%lld %.2lf
",ans,ansp);
    }
    return 0;
}
View Code

B - Chess

经典的多游戏博弈,了解sg函数即可秒之。

//
//  main.cpp
//  multi2016.1.b
//
//  Created by New_Life on 16/7/25.
//  Copyright © 2016年 chenhuan001. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;


//求下sg函数

int mark[1<<21];

void dfs(int s)
{
    int pre = -1;
    int next[22];
    memset(next,0,sizeof(next));
    for(int i=19;i>=0;i--)
    {
        if( ((1<<i)&s) != 0)
        {
            if(pre !=-1)
            {
                //跳到这里
                int nexts = (s-(1<<i))|(1<<pre);
                if(mark[nexts] == -1)
                    dfs(nexts);
                next[ mark[nexts] ] = 1;
            }
            
        }
        else pre = i;
    }
    for(int i=0;i<=20;i++)
        if(next[i] == 0)
        {
            mark[s] = i;
            return ;
        }
}

int main() {
    //满满地套路
    memset(mark,-1,sizeof(mark));
    for(int i=0;i<(1<<20);i++)
    {
        if(mark[i] != -1) continue;
        dfs(i);
    }
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        scanf("%d",&n);
        int ans = 0;
        for(int i=0;i<n;i++)
        {
            int m;
            scanf("%d",&m);
            int tmp=0;
            for(int j=0;j<m;j++)
            {
                int x;
                scanf("%d",&x);
                tmp |= (1<<(x-1));
            }
            ans ^= mark[tmp];
        }
        if(ans == 0) printf("NO
");
        else printf("YES
");
    }
    return 0;
}
View Code

D - GCD

用普通的st算法预处理出区间的gcd,然后二分即可

//
//  main.cpp
//  multi2016.1.first
//
//  Created by New_Life on 16/7/25.
//  Copyright © 2016年 chenhuan001. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <map>
#include <algorithm>
using namespace std;
#define N 100100

int dp[N][20];
int n;
int s[N];
map<int,int>cao;

int _gcd(int b,int d)
{
    if(b == 0) return d;
    return _gcd(d%b,b);
}

void RMQ_init()
{
    for(int i=1; i<=n; i++) dp[i][0]=s[i];
    for(int j=1; (1<<j)<=n; j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            dp[i][j] = _gcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int RMQ(int L,int R)
{
    int k=0;
    while((1<<(k+1))<=R-L+1) k++;
    return _gcd(dp[L][k],dp[R-(1<<k)+1][k]);
}

long long save[N];
//long long save[N];
int ans[N];


int main() {
    int T;
    int tt = 1;
    cin>>T;
    while(T--)
    {
        cao.clear();
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",s+i);
        RMQ_init();
        
        printf("Case #%d:
",tt++);
        int q;
        scanf("%d",&q);
        int id = 1;
        for(int i=0;i<q;i++)
        {
            int b,d;
            scanf("%d%d",&b,&d);
            //printf("%d %I64d
",RMQ(b,d),save[ RMQ(b, d) ]);
            int gcd=RMQ(b,d);
            //printf("%d %I64d
",RMQ(b,d),0LL);
            if(cao[gcd] == 0)
            {
                cao[gcd] = id++;
            }
            ans[i] = gcd;
        }
        
        
        memset(save, 0, sizeof(save));
        for(int i=1;i<=n;i++)
        {
            int tmp = s[i];
            int b = i, d = n;
            do
            {
                int preb = b;
                d = n;
                tmp = RMQ(i, b);//
                while(b<d)
                {
                    int mid = (b+d+1)/2;
                    if(RMQ(i,mid)<tmp) d = mid-1;
                    else b = mid;
                }
                int tid = cao[tmp];
                if(tid != 0)
                {
                    save[ tid ] += (b-preb+1);//搞错这个了
                }
                b++;
            }while(b<=n);
            
        }
        
        for(int i=0;i<q;i++)
        {
            //printf("%d %I64d
",ans[i],save[ cao[ans[i]] ]);
            printf("%d %lld
",ans[i],save[ cao[ans[i]] ]);
        }
    }
    return 0;
}
View Code

E - Necklace

暴力枚举阴珠子的圆排列情况,然后跑匈牙利就行了

//
//  main.cpp
//  multi2016.1.E
//
//  Created by New_Life on 16/7/26.
//  Copyright &#169; 2016年 chenhuan001. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 11

int n,m;
int nolink[N][N];
int savex[N];
int mark[N];
int mark1[N];
int mat[N][N];
int ans;
int pre[N];


int dfs1(int s)
{
    for(int i=1;i<=n;i++)
    {
        if(mat[s][i] == 0 || mark[i] == 1) continue;
        mark[i] = 1;
        if(pre[i] == -1 || dfs1(pre[i]))
        {
            pre[i] = s;
            return 1;
        }
    }
    return 0;
}

void gao()
{
    //建图
    memset(mat,0,sizeof(mat));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(nolink[ savex[i] ][j]==0 && nolink[ savex[i==n?1:i+1] ][j]==0)
            {
                mat[j][i] = 1;
            }
        }
    }
    
    memset(pre, -1,sizeof(pre));
    memset(mark,0,sizeof(mark));
    int tmp=0;
    for(int i=1;i<=n;i++)
    {
        memset(mark,0,sizeof(mark));
        tmp += dfs1(i);
    }
    ans = max(ans,tmp);
}

void dfs(int s)
{
    if(s>n)
    {
        gao();
        return ;
    }
    
    for(int i=2;i<=n;i++)
    {
        if(mark1[i] == 1) continue;
        mark1[i] = 1;
        savex[s] = i;
        dfs(s+1);
        mark1[i] = 0;
    }
}

int main(int argc, const char * argv[]) {
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(nolink,0,sizeof(nolink));
        //换一换
        for(int i=0;i<m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            nolink[y][x] = 1;
        }
        //暴力枚举出X边的所有情况。
        memset(mark1,0,sizeof(mark1));
        savex[1] = 1;
        mark[1] = 1;
        ans = 0;
        dfs(2);
        printf("%d
",n-ans);
    }
    return 0;
}
/*
 3 5
 1 2
 1 3
 2 1
 2 2
 3 1
 */
View Code

F - PowMod

这题又是两个问题的叠加,第一个问题比较巧妙。把问题由某个素因子分解成子问题,妙啊,妙啊

2016 Multi-University Training Contest 1

//
//  main.cpp
//  multi2016.1.F
//
//  Created by New_Life on 16/7/26.
//  Copyright &#169; 2016年 chenhuan001. All rights reserved.
//

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <stdlib.h>
using namespace std;
#define MOD 1000000007

int phi[10000100];
long long sum[10000100];
void getphis(int maxn)
{
    phi[1]=1;
    phi[2]=1;
    for(int i=2;i<=maxn;i++) phi[i]=i;
    for(int i=2;i<=maxn;i+=2) phi[i]/=2;
    for(int i=3;i<=maxn;i+=2)
    {
        if(phi[i]==i)//为素数
        {
            for(int j=i;j<=maxn;j+=i)
            {
                phi[j]=phi[j]-phi[j]/i;
                
            }
        }
    }
    for(int i=1;i<=maxn;i++) sum[i] = (sum[i-1]+phi[i])%MOD;
}


int save[10];
int cnt=0;
long long gao(int n,int m,int sign)
{
    int tmp = -1;
    int id = 0;
    for(int i=cnt-1;i>=0;i--)
    {
        if( ((1<<i)&sign) != 0)
        {
            id = i;
            tmp = save[i];
            break;
        }
    }
    
    if(tmp == -1)
    {
        return sum[m];
    }
    if(m==1)
    {
        return phi[n];
    }
    if(m==0) return 0;
    return (gao(n,m/tmp,sign)+(long long)phi[tmp]*gao(n/tmp,m,sign-(1<<id)))%MOD;
    
}

/**************
 快速幂模板
 调用:Quk_Mul(a,b,mod)
 返回:a^b%mod
 复杂度:当mod>10^9,log(mod)*log(b),否则log(b)
 ***************/
long long Mod_Mul(long long a,long long b,long long mod)
{
    long long msum=0;
    while(b)
    {
        if(b&1) msum = (msum+a)%mod;
        b>>=1;
        a = (a+a)%mod;
    }
    return msum;
}

long long Quk_Mul(long long a,long long b,long long mod)
{
    bool qmflag=mod>1e9?1:0;
    long long qsum=1;
    while(b)
    {
        if(b&1) qsum = (qmflag==1) ? Mod_Mul(qsum,a,mod) : (qsum*a)%mod;
        b>>=1;
        a = (qmflag==1) ? Mod_Mul(a,a,mod) : (a*a)%mod;
    }
    return qsum;
}

long long fun(long long x,int p)
{
    if(p==1)
    {
        return 0;
    }
    return Quk_Mul(x, fun(x,phi[p]) + phi[p], p);
}

int main() {
    
    getphis(10000000);
    int n,m,p;
    while(scanf("%d%d%d",&n,&m,&p)!=EOF)
    {
        int tn = n;
        cnt=0;
        for(int i=2;i*i<=tn;i++)
        {
            if(tn%i == 0)
            {
                tn/=i;
                save[cnt++] = i;
            }
        }
        if(tn!=1) save[cnt++] =tn;
        long long k = gao(n,m,(1<<cnt)-1);
        
        cout<<fun(k,p)<<endl;
        
    }
    return 0;
}
/*
 1 2 6
 1 100 9

*/
View Code

G - Rigid Frameworks

题意很恐怖,想了挺久一直往找特殊规律,然后状压dp搞。 结果竟然可以看成,二分完全图的联通子图数。 仔细想想确实是可以这样转换。

然后就是一个经典的dp了。

2016 Multi-University Training Contest 1

我的做法稍有点不同,但是本质是一样的。

//
//  main.cpp
//  hdu5729
//
//  Created by New_Life on 16/7/27.
//  Copyright &#169; 2016年 chenhuan001. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;

#define N 110
#define MOD 1000000007

//组合数打表模板,适用于N<=3000
//c[i][j]表示从i个中选j个的选法。
long long C[N][N];

long long pow2[N];

void get_C(int maxn)
{
    C[0][0] = 1;
    for(int i=1;i<=maxn;i++)
    {
        C[i][0] = 1;
        for(int j=1;j<=i;j++)
        //    C[i][j] = C[i-1][j]+C[i-1][j-1];
            C[i][j] = (C[i-1][j]+C[i-1][j-1])%MOD;
    }
}


long long dp[11][11][N];

long long dfs(int n,int m,int k)
{
    if(dp[n][m][k]!=-1) return dp[n][m][k];
    if(k<n+m-1) return dp[n][m][k] = 0;
    
    long long all = C[n*m][k];
    all -= C[(n-1)*m][k];
    long long sum = 0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int p=i+j-1;p<=k;p++)
            {
                if(i+j==n+m) continue;
                long long tmp = (dfs(i,j,p)*C[n-1][i-1])%MOD;
                tmp = tmp*C[m][j]%MOD;
                tmp = tmp*C[(n-i)*(m-j)][k-p]%MOD;
                sum += tmp;
                sum %= MOD;
            }
    
    return dp[n][m][k]=((all-sum)%MOD+MOD)%MOD;
}

int main() {
    for(int i=0;i<=100;i++)
    {
        long long tmp = 1;
        for(int j=1;j<=i;j++)
            tmp = (tmp*2)%MOD;
        pow2[i] = tmp;
    }
    get_C(100);
    memset(dp,-1,sizeof(dp));
    int n,m;
    while(cin>>n>>m)
    {
        long long ans = 0;
        for(int i=1;i<=n*m;i++)
        {
            ans = ans + (dfs(n,m,i)*pow2[i])%MOD;
            ans %= MOD;
        }
        ans = (ans+MOD)%MOD;
        cout<<ans<<endl;
    }
    return 0;
}
View Code

而且这题数据最多只有10*10,所以完全可以打表啊。

写了一个打表程序,10s之内可以跑出,但是比标准解法还要复杂。--!

//
//  main.cpp
//  multi2016.1.G
//
//  Created by New_Life on 16/7/27.
//  Copyright &#169; 2016年 chenhuan001. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string>
#include <stdlib.h>
#include <algorithm>
using namespace std;


#define MOD 1000000007
typedef long long ll;

ll save[1100];
ll save1[11][11];
ll pow2[11];
ll mark[1<<10][1<<10][10][2];

ll dfs(int L,int R,int flag,ll cnt)
{
    if(mark[L][R][cnt][flag]!=-1) return mark[L][R][cnt][flag];
    if(L==0 && R==0) return 1;
    if(L==0) return mark[L][R][cnt][flag]=save1[save[R]][cnt];
    if(R==0) return mark[L][R][cnt][flag]=save1[save[L]][cnt];
    
    ll tcnt = 0;
    if(flag == 0)//左边选择
    {
        int tr = R;
        
        for(;tr!=0;tr = ((tr-1)&R) )//必定选择一个以上
        {
            tcnt += save1[ save[tr] ][cnt]*dfs(L, R-tr, flag^1, save[tr]);
            tcnt %= MOD;
        }
    }
    else//右边选择
    {
        int tl = L;
        for(;tl!=0;tl = ((tl-1)&L) )//必定选择一个以上
        {
            tcnt += save1[ save[tl] ][cnt]*dfs(L-tl, R, flag^1, save[tl]);
            tcnt %= MOD;
        }
    }
    return mark[L][R][cnt][flag]=tcnt;
}

ll C[12][12];

void get_C()
{
    C[0][0] = 1;
    for(int i=1;i<=10;i++)
    {
        C[i][0] = 1;
        for(int j=1;j<=i;j++)
            C[i][j] = C[i-1][j]+C[i-1][j-1];
    }
}

void get_fff()
{
    for(int n=1;n<=10;n++)
        for(int m=1;m<=10;m++)
        {
            ll add[110];
            for(int j=1;j<=m;j++) add[j] = C[m][j];
            ll tmp[2][110];
            memset(tmp,0,sizeof(tmp));
            int a = 0;
            tmp[a][0] = 1;
            for(int i=1;i<=n;i++)
            {
                memset(tmp[a^1],0,sizeof(tmp[a^1]));
                for(int j=100;j>=1;j--)
                {
                    for(int k=1;k<=m;k++)
                    {
                        if(j-k < 0) continue;
                        tmp[a^1][j] = (tmp[a^1][j] + tmp[a][j-k]*add[k])%MOD;
                    }
                }
                a = a^1;
            }
            
            ll sum = 0;
            for(int i=1;i<=100;i++)
            {
                sum  = (sum+pow2[i]*tmp[a][i])%MOD;
            }
            save1[n][m] = sum;
        }
}
 

long long ans[10][10]={2LL,4LL,8LL,16LL,32LL,64LL,128LL,256LL,512LL,1024LL,4LL,48LL,448LL,3840LL,31744LL,258048LL,2080768LL,16711680LL,133955584LL,72693241LL,8LL,448LL,15008LL,429568LL,11596928LL,306009088LL,2369992LL,530422352LL,523796386LL,215597769LL,16LL,3840LL,429568LL,38529024LL,207602155LL,200364260LL,160231949LL,554488410LL,190587140LL,810639015LL,32LL,31744LL,11596928LL,207602155LL,519052755LL,105908051LL,72277468LL,23284327LL,286851052LL,865424583LL,64LL,258048LL,306009088LL,200364260LL,105908051LL,126514477LL,312460128LL,711245882LL,987939142LL,374773317LL,128LL,2080768LL,2369992LL,160231949LL,72277468LL,312460128LL,25696077LL,433482528LL,357533852LL,103280608LL,256LL,16711680LL,530422352LL,554488410LL,23284327LL,711245882LL,433482528LL,162005329LL,771914613LL,811634190LL,512LL,133955584LL,523796386LL,190587140LL,286851052LL,987939142LL,357533852LL,771914613LL,585919048LL,573168128LL,1024LL,72693241LL,215597769LL,810639015LL,865424583LL,374773317LL,103280608LL,811634190LL,573168128LL,935300639LL};


int main(int argc, const char * argv[]) {
    
    for(int i=0;i<(1<<10);i++)
    {
        int tmp = 0;
        for(int j=0;j<10;j++)
        {
            if( (i&(1<<j))!=0 )
                tmp++;
        }
        save[i] = tmp;
    }
    for(int i=0;i<=100;i++)
    {
        int tmp =1 ;
        for(int j=0;j<i;j++)
        {
            tmp *= 2;
            tmp %= MOD;
        }
        pow2[i] = tmp;
    }
    //预处理3
    get_C();
    get_fff();
    memset(mark,-1,sizeof(mark));
    
    for(int n=1;n<=10;n++)
    {
        for(int m=1;m<=10;m++)
        {
            //cout<<dfs((1<<n)-1-1,(1<<m)-1,0,1)<<"LL,";
        }
    }
     
    int n,m;
    while(cin>>n>>m)
    {
        cout<<ans[n-1][m-1]<<endl;
    }
    return 0;
}
View Code

K - tetrahedron

计算几何,要是知道求四面体内切球心的公式当然可以瞬间秒,但是本弱完全不知,只能和它肛道理

找出面面之间的平分面,交点即使球心坐标。

如何面面的平分面呢。

1.直接用面面交板子,求出交线。

2.找到一个垂直于交线,且顶点在分别在两个平面上的三角形,然后求得一个面的点。

#include <iostream>
#include <cmath>
#include <stdio.h>
#include <vector>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;

#define MAX_N 110

/*------------------常量区-------------------*/

const double INF        = 1e10;      // 无穷大
const double EPS        = 1e-5;      // 计算精度
const double PI         = acos(-1.0);// PI
const int PINXING       = 0;         // 平行
const int XIANGJIAO     = 1;         // 相交
const int XIANGLI       = 0;         // 相离
const int GONGXIAN      = 2;         // 共线
const int CHONGDIE      = -1;        // 重叠
const int INSIDE        = 1;         // 点在图形内部
const int OUTSIDE       = 0;         // 点在图形外部
const int BORDER        = 2;         // 点在图形边界

/*-----------------类型定义区----------------*/

struct Point {              // 二维点或矢量
    double x, y;
    //double angle, dis;
    Point() {}
    Point(double x0, double y0): x(x0), y(y0) {}
    void read()
    {
        scanf("%lf%lf",&x,&y);
    }
};

struct Line {               // 二维的直线或线段
    Point p1, p2;
    Line() {}
    Line(Point p10, Point p20): p1(p10), p2(p20) {}
    void read()
    {
        scanf("%lf%lf%lf%lf",&p1.x,&p1.y,&p2.x,&p2.y);
    }
};

struct Rect {              // 用长宽表示矩形的方法 w, h分别表示宽度和高度
    double w, h;
    Rect() {}
    Rect(double _w,double _h) : w(_w),h(_h) {}
};
struct Rect_2 {             // 表示矩形,左下角坐标是(xl, yl),右上角坐标是(xh, yh)
    double xl, yl, xh, yh;
    Rect_2() {}
    Rect_2(double _xl,double _yl,double _xh,double _yh) : xl(_xl),yl(_yl),xh(_xh),yh(_yh) {}
};
struct Circle {            //
    Point c;
    double r;
    Circle() {}
    Circle(Point _c,double _r) :c(_c),r(_r) {}
};

typedef vector<Point> Polygon;      // 二维多边形
typedef vector<Point> Points;       // 二维点集

/*-------------------基本函数区---------------------*/

inline double max(double x,double y)
{
    return x > y ? x : y;
}
inline double min(double x, double y)
{
    return x > y ? y : x;
}
inline bool ZERO(double x)              // x == 0
{
    return (fabs(x) < EPS);
}
inline bool ZERO(Point p)               // p == 0
{
    return (ZERO(p.x) && ZERO(p.y));
}

inline bool EQ(double x, double y)      // eqaul, x == y
{
    return (fabs(x - y) < EPS);
}
inline bool NEQ(double x, double y)     // not equal, x != y
{
    return (fabs(x - y) >= EPS);
}
inline bool LT(double x, double y)     // less than, x < y
{
    return ( NEQ(x, y) && (x < y) );
}
inline bool GT(double x, double y)     // greater than, x > y
{
    return ( NEQ(x, y) && (x > y) );
}
inline bool LEQ(double x, double y)     // less equal, x <= y
{
    return ( EQ(x, y) || (x < y) );
}
inline bool GEQ(double x, double y)     // greater equal, x >= y
{
    return ( EQ(x, y) || (x > y) );
}

// 输出浮点数前,防止输出-0.00调用该函数进行修正!
inline double FIX(double x)
{
    return (fabs(x) < EPS) ? 0 : x;
}



/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
//-------------------3D 区域----------------------------//

struct Point3D {            //三维点或矢量
    double x, y, z;
    Point3D() {}
    Point3D(double x0, double y0, double z0): x(x0), y(y0), z(z0) {}
    void read()
    {
        scanf("%lf%lf%lf",&x,&y,&z);
    }
};

struct Line3D {             // 三维的直线或线段
    Point3D p1, p2;
    Line3D() {}
    Line3D(Point3D p10, Point3D p20): p1(p10), p2(p20) {}
    void read()
    {
        scanf("%lf%lf%lf%lf%lf%lf",&p1.x,&p1.y,&p1.z,&p2.x,&p2.y,&p2.z);
    }
};

struct Area3D{
    Point3D p1,p2,p3;
    Area3D(){}
    Area3D(Point3D p10, Point3D p20,Point3D p30): p1(p10), p2(p20), p3(p30){}
    void read()
    {
        p1.read(); p2.read(); p3.read();
        //scanf("%lf%lf%lf%lf%lf%lf%lf%lf%lf",&p1.x,&p1.y,&p1.z,&p2.x,&p2.y,&p2.z,&p3.x,&p3.y,&p3.z);
    }
};

inline bool ZERO(Point3D p)              // p == 0
{
    return (ZERO(p.x) && ZERO(p.y) && ZERO(p.z));
}

//////////////////////////////////////////////////////////////////////////////////////
//三维矢量运算
bool operator==(Point3D p1, Point3D p2)
{
    return ( EQ(p1.x, p2.x) && EQ(p1.y, p2.y) && EQ(p1.z, p2.z) );
}
bool operator<(Point3D p1, Point3D p2)
{
    if (NEQ(p1.x, p2.x)) {
        return (p1.x < p2.x);
    } else if (NEQ(p1.y, p2.y)) {
        return (p1.y < p2.y);
    } else {
        return (p1.z < p2.z);
    }
}
Point3D operator+(Point3D p1, Point3D p2)
{
    return Point3D(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z);
}
Point3D operator-(Point3D p1, Point3D p2)
{
    return Point3D(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z);
}
Point3D operator * (const Point3D& A, double p) { return Point3D(A.x*p, A.y*p, A.z*p); }
Point3D operator / (const Point3D& A, double p) { return Point3D(A.x/p, A.y/p, A.z/p); }

Point3D operator*(Point3D p1, Point3D p2) // 计算叉乘 p1 x p2
{
    return Point3D(p1.y * p2.z - p1.z * p2.y,
                   p1.z * p2.x - p1.x * p2.z,
                   p1.x * p2.y - p1.y * p2.x );
}
double operator&(Point3D p1, Point3D p2) { // 计算点积 p1·p2
    return (p1.x * p2.x + p1.y * p2.y + p1.z * p2.z);
}
double Norm(Point3D p) // 计算矢量p的模
{
    return sqrt(p.x * p.x + p.y * p.y + p.z * p.z);
}

//取平面法向量
Point3D GetV(Area3D s){
    return (s.p1-s.p2)*(s.p2-s.p3);
}

//判三点共线
int PointOnLine(Point3D p1,Point3D p2,Point3D p3){
    return ZERO( (p1-p2)*(p2-p3) );
}


//判四点共面
int PointOnArea(Point3D a,Point3D b,Point3D c,Point3D d){
    return ZERO( GetV(Area3D(a, b, c))&(d-a) );
}

//求三维空间中两点间的距离
double Dis(Point3D p1, Point3D p2)
{
    return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)+(p1.z-p2.z)*(p1.z-p2.z));
}
// 求三维空间中点到直线的距离
double Dis(Point3D p, Line3D L)
{
    if(L.p1==L.p2) return Dis(p, L.p1);
    return Norm((p - L.p1) * (L.p2 - L.p1)) / Norm(L.p2 - L.p1);
}

bool OnLine(Point3D p, Line3D L) // 判断三维空间中点p是否在直线L上
{
    if(L.p1==L.p2 && p==L.p1) return true;//共点时,返回true
    return ZERO( (p - L.p1) * (L.p2 - L.p1) );
}
bool OnLineSeg(Point3D p, Line3D L) // 判断三维空间中点p是否在线段l上
{
    return ( ZERO((L.p1 - p) * (L.p2 - p)) &&
            EQ( Norm(p - L.p1) + Norm(p - L.p2), Norm(L.p2 - L.p1)) );
}

// 点p到平面Ap-Al的距离。
double Dis(Point3D p, Point3D Ap, Point3D Al) {
    return fabs((p-Ap)&Al)/Norm(Al); // 如果不取绝对值,得到的是有向距离
}

// 点p在平面Ap-Al上的投影。
Point3D PointToArea(Point3D p,Point3D Ap, Point3D Al) {
    Al=Al/(Norm(Al));//把Al变成法向量。
    return p-Al*((p-Ap)&Al);
}
//得到点p到直线L的距离,并返回p到直直线L的最近点rep
double PointToLine(Point3D p,Line3D L,Point3D& rep)
{
    if(L.p1==L.p2)
    {
        rep=L.p1;
        return Dis(p,L.p1);
    }
    Point3D a,b;
    a = L.p2-L.p1;
    b = p-L.p1;
    double dis12 = Dis(L.p1,L.p2);
    double dis = ( Norm(a*b) )/dis12;
    
    double k = (a&b)/(Norm(a)*dis12) ;
    rep = L.p1+(L.p2-L.p1)*k;
    return dis;
}
//求两条直线之间的关系(三维)
//输入:两条不为点的直线
//输出:相交返回XIANGJIAO和交点p,平行返回PINGXING,共线返回GONGXIAN
int LineAndLine(Line3D L1,Line3D L2,Point3D &p)
{
    Point3D px,py;
    px = L1.p1 - L1.p2;
    py = L2.p1 - L2.p2;
    
    if( ZERO(px*py) )//平行或者共线
    {
        if( ZERO( (L2.p1-L1.p1)*py ) ) //共线
        {
            return GONGXIAN;
        }
        return PINXING;
    }
    //判断是否共面
    Point3D tp=(L1.p1-L2.p1)*py;
    if( !ZERO(tp&px) ) return XIANGLI;//XIANGLI与平行相同
    
    p = L1.p1;
    Point3D tp1=(L2.p1-L1.p1)*(L2.p1-L2.p2);
    Point3D tp2=(L1.p2-L1.p1)*(L2.p1-L2.p2);
    double _t = Norm(tp1)/Norm(tp2);
    //tp1和tp2肯定是共线的,如果反向则_t 为负
    if( LT( (tp1&tp2),0 ) ) _t*=-1;
    p.x += (L1.p2.x-L1.p1.x)*_t;
    p.y += (L1.p2.y-L1.p1.y)*_t;
    p.z += (L1.p2.z-L1.p1.z)*_t;
    return XIANGJIAO;
}

//空间两直线最近点对。直线不能平行,直线不能为点.
//ans1为直线a1,b1上的最近点
Point3D ans1,ans2;
double SegSegDistance(Point3D a1, Point3D b1, Point3D a2, Point3D b2)
{
    Point3D v1 = (a1-b1), v2 = (a2-b2);
    Point3D N = v1*v2;
    Point3D ab = (a1-a2);
    double ans = (N&ab) / Norm(N);
    Point3D p1 = a1, p2 = a2;
    Point3D d1 = b1-a1, d2 = b2-a2;
    double t1, t2;
    t1 = ((p2-p1)*d2 )&(d1*d2);
    t2 = ((p2-p1)*d1 )&(d1*d2);
    double dd = Norm( (d1*d2) );
    t1 /= dd*dd;
    t2 /= dd*dd;
    ans1=a1+(b1-a1)*t1;
    ans2=a2+(b2-a2)*t2;
    return fabs(ans);
}

//直线与平面交
int LineAndArea(Line3D l1,Point3D Ap,Point3D Al,Point3D &p)
{
    //输入直线,和平面的点法式
    //第一步,判断直线与平面是否平行。
    if( ZERO((l1.p2-l1.p1)&Al)  ) return 0;//直线与平面平行。
    double _t =( (Ap-l1.p1)&Al ) / ((l1.p1-l1.p2)&Al);
    p = l1.p1+(l1.p1-l1.p2)*_t;
    return 1;
}

void dfs(int x,double &len)
{
    len++;
    dfs(x-1,len);
    dfs(x-2,len);
}

//空间两直线最近点对
//注意:直线不能平行
double LineAndLine(Line3D l1,Line3D l2,Point3D &p1,Point3D &p2)
{
    //先求出法向量
    Point3D v1,v2;
    v1 = l1.p2-l1.p1;
    v2 = l2.p2-l2.p1;
    Point3D vt=v1*v2;
    //然后先把l2投影到 l1所在的平面上
    double len = ((l2.p1-l1.p1)&vt)/Norm(vt);
    double normvt = -len/Norm(vt);
    
    vt.x = vt.x*normvt;
    vt.y = vt.y*normvt;
    vt.z = vt.z*normvt;
    
    Line3D tl2;
    tl2.p1 = l2.p1+vt;
    tl2.p2 = l2.p2+vt;
    
    int sign=LineAndLine(l1, tl2, p1);
    /*
     //测试用
     if(sign!=XIANGJIAO)
     {
     int x=0;
     printf("%lf
",len/x);
     dfs(100000000,len);
     }
     */
    return fabs(len);
}

//已知空间四面体6条边,求体积
double P( double a,double b,double c,double d,double e ){ return a*(b*c-d*e); }
double Get4V(int OA,int OB,int OC,int AB,int CA,int BC)
{
    OA*=OA;OB*=OB;OC*=OC;AB*=AB;CA*=CA;BC*=BC;
    double ans=0;
    ans+=P( OA,OB,OC,(OB+OC-BC)/2.0,(OB+OC-BC)/2.0 );
    ans-=P( (OA+OB-AB)/2.0,(OA+OB-AB)/2.0,OC,(OA+OC-CA)/2.0,(OB+OC-BC)/2.0 );
    ans+=P( (OA+OC-CA)/2.0,(OA+OB-AB)/2.0,(OB+OC-BC)/2.0,OB,(OA+OC-CA)/2.0);
    return sqrt(ans/36);
}

//求两面相交,平行或共面返回PINGXING,否则返回XIANGJIAO和直线rel
int AreaAndArea(Area3D a1,Area3D a2,Line3D &rel)
{
    Point3D va1 = GetV(a1),va2 = GetV(a2);
    Point3D lv = va1*va2;//相交直线的方向向量
    if( ZERO(lv) )//平行
    {
        return PINXING;
    }
    //然后得到某一个交点
    Point3D p1;
    if(LineAndArea(Line3D(a1.p1,a1.p2), a2.p1, va2, p1) == 0)
       if(LineAndArea(Line3D(a1.p1,a1.p3), a2.p1, va2, p1) == 0)
           LineAndArea(Line3D(a1.p2,a1.p3), a2.p1, va2, p1);
    rel.p1 = p1; rel.p2 = p1 + (lv*10);
    return XIANGJIAO;
}
//////////////////////////////////////////////////////////////////////////////////////


/*---------------------代码区---------------------------*/

int equal3(Point3D p1,Point3D p2)
{
    return p1.x==p2.x&&p1.y==p2.y&&p1.z==p2.z;
}

Area3D gettwoplanein(Area3D p1,Area3D p2,Point3D a,Point3D b)
{
    Line3D l;
    l.p1 = a; l.p2 = b;
    Point3D pf1;//p1 的法向量
    pf1 = GetV(p1);
    //找一个不在交线上的点
    Point3D p1d,p2d;
    if( (!equal3(p1.p1,a)) && (!equal3(p1.p1, b)) ) p1d=p1.p1;
    if( (!equal3(p1.p2,a)) && (!equal3(p1.p2, b)) ) p1d=p1.p2;
    if( (!equal3(p1.p3,a)) && (!equal3(p1.p3, b)) ) p1d=p1.p3;
    
    if( (!equal3(p2.p1,a)) && (!equal3(p2.p1, b)) ) p2d=p2.p1;
    if( (!equal3(p2.p2,a)) && (!equal3(p2.p2, b)) ) p2d=p2.p2;
    if( (!equal3(p2.p3,a)) && (!equal3(p2.p3, b)) ) p2d=p2.p3;
    //然后对应到l上去
    Point3D p1e,p2e;
    PointToLine(p1d, l, p1e);
    PointToLine(p2d, l, p2e);
    p2d = p2d + (p1e-p2e);
    
    //然后就是得到面上的一点
    
    double l1 = Dis(p1d, p1e);
    double l2 = Dis(p1e, p2d);
    double l3 = Dis(p1d, p2d);
    double shita = acos( (l1*l1+l2*l2-l3*l3)/(2*l1*l2) );
    shita/=2;
    double beta = acos( (l1*l1+l3*l3-l2*l2)/(2*l1*l3) );
    beta = PI -shita -beta;
    
    double x1 = l1*sin(shita)/sin(beta);
    
    double x2 = l3-x1;
    
    Point3D pp;
    pp.x = p1d.x + (p2d.x-p1d.x)*x1/(x1+x2);
    pp.y = p1d.y + (p2d.y-p1d.y)*x1/(x1+x2);
    pp.z = p1d.z + (p2d.z-p1d.z)*x1/(x1+x2);
    
    Area3D p3;
    p3.p1 = a; p3.p2 = b; p3.p3 = pp;
    return p3;
}

int main()
{
    Point3D A,B,C,D;
    while(scanf("%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",&A.x,&A.y,&A.z,&B.x,&B.y,&B.z,&C.x,&C.y,&C.z,&D.x,&D.y,&D.z)!=EOF)
    {
        Area3D ABC,ABD,BCD,ACD;
        ABC.p1 = A; ABC.p2 = B; ABC.p3 = C;
        ABD.p1 = A; ABD.p2 = B; ABD.p3 = D;
        BCD.p1 = B; BCD.p2 = C; BCD.p3 = D;
        ACD.p1 = A; ACD.p2 = C; ACD.p3 = D;
        //step one, 判断是存在四面体
        if( PointOnArea(A,B,C,D) )
        {
            printf("O O O O
");
            continue;
        }
        // 先得到三个角平分面
        Area3D p1,p2,p3;
        p1 = gettwoplanein(ABD, BCD, B, D);//讲道理,现在已经求出
        p2 = gettwoplanein(ABC, BCD, B, C);
        p3 = gettwoplanein(ABC, ACD, A, C);
        
        //然后求三个面的交点
        
        Line3D ll;
        AreaAndArea(p1, p2, ll);
        
        Point3D ans;
        LineAndArea(ll, p3.p1, GetV(p3), ans);
        double dis = Dis(ans, ABC.p1,GetV(ABC));
        printf("%.4lf %.4lf %.4lf %.4lf
",ans.x,ans.y,ans.z,dis);
    }
    return 0;
}
View Code

相关推荐