• 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

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

    //
    //  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了。

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

    //
    //  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
  • 相关阅读:
    ASP.Net请求小周期
    创建型设计模式
    eml文件解析实例,简历信息抓取工具
    Microsoft ReportViewer 控件类型版本兼容问题及解决方法
    WCF IIS 部署错误处理
    如何使Wpf浏览器应用程序被完全信任运行
    Server 2008 r2 多用户远程桌面配置
    The configuration section 'system.serviceModel' cannot be read because it is missing a section decla
    spss C# 二次开发 学习笔记(六)——Spss统计结果的输出
    spss C# 二次开发 学习笔记(二)——Spss以及统计术语解释(IT人眼中的统计术语)
  • 原文地址:https://www.cnblogs.com/chenhuan001/p/5713617.html
Copyright © 2020-2023  润新知