湖南师范大学2021年4月15日蓝桥冲刺赛解题报告与标程

A

题目描述

给一个阶幻方的残余,对齐进行还原。如果答案不止一个,输出`Too Many·

解法

显然深搜即可。
原题没有说失败如何,应该是保证至少有一组解。
另外需要注意如果输入本来就是完整的幻方,要能正确输出。

标程

#include <bits/stdc++.h>
using namespace std;

bool Flag[10];
int A[3][3];
int Ans;

inline bool checkRow(int r){
    return 15 == A[r][0] + A[r][1] + A[r][2];
}

inline bool checkCol(int c){
    return 15 == A[0][c] + A[1][c] + A[2][c];
}

/// 参数为true表示反对角线
inline bool checkDiag(bool isAnti=false){
    return isAnti ? 15 == A[2][0] + A[1][1] + A[0][2]
        : 15 == A[0][0] + A[1][1] + A[2][2];
}

int T[3][3];
/// depth表示已经放好的数
void dfs(int depth){
    if(9==depth){
        if(checkCol(2)&&checkRow(2)&&checkDiag()){ // 固定答案
            ++Ans;
            for(int i=0;i<3;++i)copy(A[i],A[i]+3,T[i]);
            /*
            printf("%d %d %d\n%d %d %d\n%d %d %d\n"
               ,T[0][0],T[0][1],T[0][2]
               ,T[1][0],T[1][1],T[1][2]
               ,T[2][0],T[2][1],T[2][2]);//*/
        }
        return;
    }

    int r=depth/3, c=depth%3;
    if(A[r][c]){ // 如果有数,直接检查然后递归
        if(2==c&&!checkRow(r)) return;
        if(2==r&&!checkCol(c)) return;
        if(2==r&&0==c&&!checkDiag(true)) return;
        dfs(depth+1);
        return;
    }

    for(int tmp,i=1;i<=9;++i){
        if(Flag[i]) continue;

        tmp = Flag[A[r][c] = i] = true;
        if(2==c&&!checkRow(r)) tmp=0;
        if(2==r&&!checkCol(c)) tmp=0;
        if(2==r&&0==c&&!checkDiag(true)) tmp=0;
        if(tmp){ // 递归
            dfs(depth+1);
            if(Ans>1) return;
        }
        A[r][c] = Flag[i] = false;
    }
}

void solute(){
    memset(Flag,0,sizeof(Flag));
    for(int i=0;i<3;++i)for(int j=0;j<3;++j){
        scanf("%d",A[i]+j);
        Flag[A[i][j]] = true;
    }

    Ans = 0;
    dfs(0);
    if(1==Ans){
        printf("%d %d %d\n%d %d %d\n%d %d %d\n"
               ,T[0][0],T[0][1],T[0][2]
               ,T[1][0],T[1][1],T[1][2]
               ,T[2][0],T[2][1],T[2][2]);
    }else{
        puts("Too Many");
    }
}
int main(){
    //freopen("1.txt","r",stdin);
    solute();
    return 0;
}

B

题目描述

给定正整数,输出对应的Excel的列号。
Excel用A~Z表示第1~26列,第27列用AA表示,以此类推。

解法

很明显跟26进制数有关,不过比较直观的做法是从最高位开始做,所以先找到高位,然后往低位递归即可。当然也可以迭代实现。

标程

#include <bits/stdc++.h>
using namespace std;
typedef long long llt;

llt POW[8] = {1}; // 7次方就超过20亿
char A[8];

/// 确定第depth位,0为最高位,直到k位, n为序数,从0开始
void dfs(int depth, int k, int n){
    if(depth==k){
        return;
    }
    llt unit = POW[k-depth-1];
    llt t = n / unit;
    A[depth] = t + 'A';
    dfs(depth+1, k, n%unit);
}
void solute(){
    for(int i=1;i<8;++i)POW[i]=26*POW[i-1];

    llt n;
    cin>>n;
    --n;

    int k = 1;
    while(n>=POW[k]) n-=POW[k++];

    /// 长度为k,第n个,从0开始数
    A[k] = '\0';
    dfs(0, k, n);
    puts(A);
}
int main(){
    //freopen("1.txt","r",stdin);
    solute();
	return 0;

}

C

题目描述

给一个正则表达式,内容只包含x,运算符只包含()|,求能够匹配的字符串的最大长度。题目保证输入合法。

解法

这个属于语法语义问题,当然其实不需要编译的相关理论,因为题目保证输入格式一定合法,所以可以直接设计递归。
这里写了一个算符优先分析方法,经过词法分析、语法分析和语义分析,最后算出答案。
词法分析把连续的 x以及运算符转成相应的编码,并且添加了+
语法分析把中缀表达式转成了后缀表达式。
语义分析就是用一个栈去计算后缀表达式。
但这段代码放到蓝桥杯是有问题的,因为蓝桥杯的样例里面有两组数据格式是不合法的,多了一个(。所以如果你设计了一个完整的可以检错的代码反而过不了。

标程

#include <bits/stdc++.h>
using namespace std;

#define pb push_back
typedef vector<int> vi;
vi Token;
vi Parse;

void lex(string&s){
    Token.clear();
    int k = 0, n = s.length();
    while(k<n){
        if('x'==s[k]){
            if(k>0&&')'==s[k-1]) Token.pb(-3);

            int tmp = 0;
            while('x'==s[k]) ++k, ++tmp;
            Token.pb(tmp);

            if(k<n&&'('==s[k]) Token.pb(-3);
            if(k==n) break;
        }

        if('('==s[k]){
            if(k>0&&')'==s[k-1]) Token.pb(-3);
            Token.pb(-1);
        }else if(')'==s[k])Token.pb(-2);
        else if('|'==s[k])Token.pb(-4);
        else throw runtime_error("XXXX");
        ++k;
    }
    Token.pb(-5);
}

int const GT = 1;
int const LT = 2;
int const EQ = 4;
int const AC = 8;
const int TABLE[6][6] = {
    AC,LT,LT,LT,LT,LT,
    GT,LT,LT,GT,LT,LT,
    GT,GT,LT,GT,LT,LT,
    GT,GT,GT,GT,0, 0,
    GT,LT,LT,EQ,LT,LT,
    GT,GT,GT,GT,GT,0
};

void parse(){
    vi st;
    int k = 0;
    st.pb(-5);
    while(1){
        int aa = *--st.end();
        int a = aa > 0 ? 5 : aa+5;
        int bb = Token[k];
        int b = bb > 0 ? 5 : bb+5;
        if(TABLE[a][b]&GT){
            Parse.pb(aa);
            st.pop_back();
        }else if(TABLE[a][b]&LT){
            ++k;
            st.pb(bb);
        }else if(TABLE[a][b]&EQ){
            ++k;
            st.pop_back();
        }else if(TABLE[a][b]&AC){
            return;
        }else{
            throw runtime_error("ZZZZ");
        }
    }
}
int calc(){
    vi st;
    for(int i=0,n=Parse.size();i<n;++i){
        if(Parse[i]>0){
            st.pb(Parse[i]);
        }else if(-3==Parse[i]){ // 连接
            int b = *--st.end();
            st.pop_back();
            int a = *--st.end();
            st.pop_back();
            st.pb(a+b);
        }else if(-4==Parse[i]){ // 或者
            int b = *--st.end();
            st.pop_back();
            int a = *--st.end();
            st.pop_back();
            st.pb(max(a,b));
        }else{
            // throw runtime_error("UUU");
            // do nothing
        }
    }
    //if(1!=st.size()) throw runtime_error("OOOOO");
    return *--st.end();
}
/*
void disp(const vi&v){
    for(auto i:v)cout<<i<<" ";
    cout<<endl;
}//*/
void solute(){
    string s;
    cin>>s;

    /// 词法分析
    lex(s);
    //disp(Token);
    /// 语法分析
    parse();
    //disp(Parse);
    /// 语义分析
    cout<<calc()<<endl;
}
int main(){
    //freopen("1.txt","r",stdin);
    solute();
    return 0;
}

D

题目描述

N N N个数,求这个 N N N个数不能线性组合出的数的数量。

解法

背包即可,也可以用母函数。 g c d gcd gcd不为 1 1 1存在无穷不能组合的数,否则公倍数以上的数一定能够组合出来。
注意特殊情况。

标程

#include <bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
typedef vector<int> vi;

int gcd(int a,int b){
    return b ? gcd(b,a%b) : a;
}

/// 无穷背包
void knapsak(int d[],int maxv,int cost){
    for(int v=cost;v<=maxv;++v){
        d[v] = max(d[v], d[v-cost]+1);
    }
}

int D[11112];
void solute(){
    int n;
    scanf("%d",&n);
    int a[110];
    for(int i=0;i<n;++i)scanf("%d",a+i);
    sort(a,a+n);
    n = unique(a,a+n) - a;

    if(1==n){
        puts(1==a[0]?"0":"INF");
        return;
    }
    int g = gcd(a[0],a[1]);
    for(int i=2;i<n;++i)g = gcd(g,a[i]);
    if(1!=g){
        puts("INF");
        return;
    }

    fill(D,D+11111,-1000000000);
    D[0] = 0;
    for(int i=0;i<n;++i)knapsak(D,11111,a[i]);
    int ans = 0;
    for(int i=1;i<=11111;++i)if(D[i]<0)++ans;
    printf("%d\n",ans);
}

int main(){
    // freopen("1.txt","r",stdin);
    solute();
    return 0;
}

E

题目描述

若干种作物,每个作物有一个成熟所花费的时间。还有若干种杂交方案,格式为: A A A B B B两种作物一起种,可以得到作物 C C C,所花时间为 A A A B B B中较长的成熟时间。
现在给定初始拥有的作物(每种作物无限个),再给定目标作物,问最少要几天才能得到目标作物。

解法

咋一看像是图论。不过用最简单的搜索即可。搜索中可以发现其实可以做成 D P DP DP,时间更优。当然,还是用递归来实现。
D i D_i Di是第 i i i中作物的最短时间,如果 i i i是最初给定的作物,则 D i = 0 D_i=0 Di=0,否则, i i i由杂交方案给出。其时间为:
D i = m i n ( D a , D b ) + m a x ( W a , W b ) D_i=min(D_a, D_b)+max(W_a,W_b) Di=min(Da,Db)+max(Wa,Wb)
其中 W i W_i Wi为第 i i i中植物的成熟时间,这是题目给定的。当然这只是一种方案规定的时间,最后的 D i D_i Di应该是所有方案中最小的那个。

标程

#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mp make_pair
#define fi first
#define se second

typedef vector<int> vi;
typedef pair<int,int> pii;
typedef vector<pii> vpii;

int const INF = 0x3F3F3F3F;

int N,M,K,T;
int A[2100],B[2100];
vpii F[2100];
int D[2100];

/// 搜索要得到t最少用多少时间
int dfs(int t){
    if(-1!=D[t]) return D[t];

    /// 找所有跟t有关的规则
    const vpii& vec = F[t];
    int tmp = INF;
    for(int a,b,i=0,n=vec.size();i<n;++i){
        a = vec[i].fi;
        b = vec[i].se;
        tmp = min(tmp, max(A[a], A[b]) + max(dfs(a), dfs(b)));
    }
    return D[t] = tmp;
}

void solute(){
    memset(D,-1,sizeof(D));

    scanf("%d%d%d%d",&N,&M,&K,&T);
    for(int i=1;i<=N;++i){
        scanf("%d",A+i);
        F[i].clear();
    }
    for(int a,i=1;i<=M;++i){
        scanf("%d",&a);
        D[B[i]=a] = 0;
    }
    for(int a,b,c,i=1;i<=K;++i){
        scanf("%d%d%d",&a,&b,&c);
        F[c].pb(mp(a, b));
    }
    printf("%d\n", dfs(T));
}
int main(){
    //freopen("1.txt","r",stdin);
    solute();
    return 0;
}

F

题目描述

解法

标程

G

题目描述

n n n个人参加考试,分配考场,同一个考场不能有两个人互相认识,问最少几个考场。

解法

看起来像是匹配问题与最大独立集问题,但是不是。
很明显等价于图染色问题。每个顶点染一个颜色,相邻点不能同色,问至少要多少种颜色。

标程

H

题目描述

给定 n n n个数,问最多从中间挑多少个数出来,使得任意两个数相差不为 k k k k k k是已知数。

解法

看起来像是匹配问题与最大独立集问题,但是又不是。
k k k不为零时,可以将 n n n个数按照模 k k k分为 k k k个集合,显然只需考虑每个集合内部即可。
每个集合内部做一个排序,则第 i i i个数值选择与否只与前一个数有关(当然有可能无关)。做一个 D P DP DP即可。
D i D_i Di为到第 i i i个数值,能够得到的最多数量。

标程

#include <bits/stdc++.h>
using namespace std;

#define pb push_back
typedef vector<int> vi;

int const SIZE = 100000+10;
int A[SIZE];
vi V[SIZE];
int Cnt[SIZE];
int D[SIZE];

void solute(){
    memset(Cnt,0,sizeof(Cnt));

    int n, k;
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;++i){
        scanf("%d",A+i);
        ++Cnt[A[i]];
    }

    if(0==k){
        int ans = 0;
        for(int i=0;i<SIZE;++i) ans += Cnt[i]?1:0;
        printf("%d\n", ans);
        return;
    }

    for(int i=0;i<k;++i)V[i].clear();

    sort(A,A+n);
    for(int i=0;i<n;++i){
        V[A[i]%k].pb(A[i]);
    }
    for(int i=0;i<k;++i)V[i].erase(unique(V[i].begin(),V[i].end()),V[i].end());

    int ans = 0;
    for(int i=0;i<k;++i)if(!V[i].empty()){
        if(1==V[i].size()){
            ans += Cnt[V[i][0]];
            continue;
        }

        const vi& v = V[i];

        D[0] = Cnt[v[0]]; // 选第0个
        D[1] = (v[1]-v[0]==k) ? max(Cnt[v[0]], Cnt[v[1]]) : Cnt[v[1]] + D[0];
        for(int j=2,n=v.size();j<n;++j){
            if(v[j]-v[j-1]==k){ // 只能二选一
                D[j] = max(D[j-1],D[j-2]+Cnt[v[j]]);
            }else{
                D[j] = D[j-1] + Cnt[v[j]];
            }
        }
        ans += D[v.size()-1];
    }
    printf("%d\n",ans);
}
int main(){
    //freopen("1.txt","r",stdin);
    solute();
    return 0;
}

I

题目描述

给定两个字符串,要求按照一定走法从第一个字符串变成第二个字符串。求最少步数。

解法

显然广搜即可。注意两个字符串相等的情况。

标程

#include <bits/stdc++.h>
using namespace std;

#define mp make_pair
#define fi first
#define se second

typedef pair<string,int> psi;
set<string> Set;
string Src,Dst;

int bfs(){
    if(Src==Dst) return 0;

    Set.clear();
    queue<psi> q;
    q.push(mp(Src,0));
    Set.insert(Src);

    while(!q.empty()){
        psi h = q.front();q.pop();
        
        /// 找到*的位置
        int pos = find(h.fi.begin(),h.fi.end(),'*') - h.fi.begin();
        /// 一共有6种可能性
        for(int i=1;i<=3;++i){
            if(pos-i>=0){
                string s(h.fi);
                swap(s[pos],s[pos-i]);
                if(s==Dst) return h.se + 1;
                if(Set.find(s)==Set.end()){
                    q.push(mp(s, h.se+1));
                    Set.insert(s);
                }
            }
            if(pos+i<h.fi.length()){
                string s(h.fi);
                swap(s[pos],s[pos+i]);
                if(s==Dst) return h.se + 1;
                if(Set.find(s)==Set.end()){
                    q.push(mp(s, h.se+1));
                    Set.insert(s);
                }
            }
        }
    }
}
void solute(){
    cin>>Src>>Dst;
    cout<<bfs()<<endl;
}
int main(){
    // freopen("1.txt","r",stdin);
    solute();
    return 0;
}

J

题目描述

求三角形与椭圆的公共面积。虽然没有直接联系,但是先看看简单多边形与圆相交求面积。如果三角形跟圆相交的面积求不出来,也不用去考虑椭圆了。虽然二者也没啥先后顺序。

解法

基本想法也是一样的。一步一步完成功能,最后求整个。
首先求出一边为x正半轴一边任意的椭圆"扇形"的有向面积,
然后再求任意边的椭圆“扇形”有向面积,
然后求出任意线段 A B AB AB与椭圆中心 O O O构成的三角形与椭圆相交的有向面积。
最后可求任意三角形或者多边形与椭圆相交的有向面积。
其中求椭圆“扇形”面积要积分,可以参考定积分的计算与辛普森积分及龙贝格积分,因为直接计算可能会超时。
另外由于题目给定的初始条件,所以要对坐标进行旋转变换。二维空间旋转其实比较简单,随便查一下资料就行。

标程

#include <bits/stdc++.h>
using namespace std;

/// some macro and functions
double const EPS = 1E-6;
double const PI = acos(-1.0);

inline int sgn(double x){return x>EPS?1:(x<-EPS?-1:0);}
inline bool is0(double x){return 0==sgn(x);}
inline bool isEq(double x,double y){return is0(x-y);}

inline double myasin(double x){
    if(x>1) return 0.5*PI;
    if(x<-1) return -0.5*PI;
    return asin(x);
}
inline double myacos(double x){
    if(x>1) return 0.0;
    if(x<-1) return PI;
    return acos(x);
}

double simpson(double(*f)(double),double a,double b){
    double mid = ( a + b ) * 0.5;
    return (b-a)*(f(a)+f(b)+4.0*f(mid)) / 6.0;
}
//自适应辛普森积分的递归调用
double asr(double(*f)(double),double a,double b,double eps,double ans){
    double mid = ( a + b ) * 0.5;
    double lans = simpson(f,a,mid);
    double rans = simpson(f,mid,b);
    //精度够直接返回
    if(fabs(lans+rans-ans)<=eps) return ans;
    //精度不够递归调用
    return asr(f,a,mid,eps,lans) + asr(f,mid,b,eps,rans);
}
//自适应辛普森积分,区间[a, b],精度eps,原函数f
double asr(double (*f)(double),double a,double b,double eps){
    return asr(f,a,b,eps,simpson(f,a,b));
}
double AA,BB;
double f(double x){
    return sqrt(1.0-(x*x)/(AA*AA));
}
/**
 * 计算椭圆面积的定积分,从x1积到x2,theta用来控制正负
 * 椭圆由a、b决定
 * 使用自适应辛普森积分,暴力法时间太大
*/
double integral(double x1, double x2, double theta, double a, double b){
    AA = a, BB = b;
    return sgn(theta)>=0 ? b*asr(f,x1,x2,EPS) : -b*asr(f,x1,x2,EPS);
}

/**
 * 给定椭圆,计算扇形的有向面积
 * 椭圆由 x^2/a^2 + y^2/b^2=1 决定且 a > b
 * 椭圆扇形的起止角度为[0, theta]
 * theta的取值范围为[-PI,PI)
*/
double calAreaOfSectorOfEllipse(double theta,double a,double b){
    if(isEq(theta, 0.5*PI)) return 0.25 * PI * a * b;
    if(isEq(theta, -0.5*PI)) return -0.25 * PI * a * b;

    /// 首先确定扇形的顶点
    double px = a*b / sqrt(a*a*tan(theta)*tan(theta)+b*b);
    double py = px * tan(theta);
    /// 判断是否要变号
    if(sgn(theta - 0.5*PI)>0 || sgn(0.5*PI + theta)<0) px = -px, py = -py;

    /// 其次计算定积分, 从px积到a
    double area1 = integral(px, a, theta, a, b);
    /// 计算三角形的面积,(0,0)(px,0)(px,py)三个顶点,有方向
    /// 一共有4个象限,可以发现刚好一致,所以无需讨论,直接相加即可
    double area2 = 0.5 * px * py;
    return area1 + area2;
}

/**
 * 给定椭圆,计算扇形的有向面积
 * 椭圆由 x^2/a^2 + y^2/b^2=1 决定且 a > b
 * 椭圆扇形的起止角度为[from, to]
 * 角度的取值范围为[-PI,PI)
 * from和to相差不会超过PI
 * 顺时针返回负,逆时针返回正
*/
double calAreaOfSectorOfEllipse(double from, double to, double a,double b){
    double tmp = calAreaOfSectorOfEllipse(to, a, b) - calAreaOfSectorOfEllipse(from, a, b);
    /// 夹角超过180,实际计算的是大块面积,需要返回的实际上是小块的面积
    double tt = fabs(from-to);
    if(sgn(tt-PI)>0) tmp = tmp - PI * a * b;
    return tmp;
}

/**
 * 解一元二次方程 ax^2+bx+c=0
 * 返回根的数量,且小根为x1,大根为x2
*/
int root(double a,double b,double c,double&x1,double&x2){
    double delta = b*b-4.0*a*c;
    int tmp = sgn(delta);

    if(tmp<0) return 0;

    if(0==tmp){
        x1 = x2 = -b / ( a + a );
        return 1;
    }

    delta = sqrt(delta);
    x1 = ( -b - (delta) ) / ( a + a );
    x2 = ( -b + (delta) ) / ( a + a );
    return 2;
}

/**
 * 给定椭圆O和线段PQ,求椭圆O与AB的相交有向面积
 * 椭圆由 x^2/a^2 + y^2/b^2=1 决定且 a > b
 * OPQ保证为逆时针方向
 * 设交点为XY,则 x = (1-t)Px + tQx,满足椭圆方程
 * 则有一个关于t的一元二次方程
*/
double calAreaOfSegAndEllipse(double a,double b,double px,double py,double qx,double qy){
    /// 首先简单判断一下是否共线
    double ccc = qx * py - px * qy;
    int tmp = sgn(ccc);
    if(0==tmp) return 0.0;

    /// 首先确定方程的系数
    double a2 = b*b*(qx-px)*(qx-px)+a*a*(qy-py)*(qy-py);
    double a1 = b*b*(2.0*(qx-px)*px)+a*a*(2.0*(qy-py)*py);
    double a0=b*b*px*px+a*a*py*py-a*a*b*b;
    double t1, t2;
    int k = root(a2,a1,a0,t1,t2);

    /// 确定角度
    double ptheta = atan2(py, px);
    double qtheta = atan2(qy, qx);

    if(k<=1){ // 计算扇形面积即可
        return calAreaOfSectorOfEllipse(ptheta, qtheta, a, b);
    }

    if(sgn(t2)<=0){ // 说明两个交点都在PQ的反向延长线上
        return calAreaOfSectorOfEllipse(ptheta, qtheta, a, b);
    }

    if(sgn(t1-1)>=0){ // 说明两个交点都在PQ延长线上
        return calAreaOfSectorOfEllipse(ptheta, qtheta, a, b);
    }

    if(sgn(t2-1)>=0&&sgn(t1<=0)){ // 说明两个交点分居两边
        return 0.5 * (px*qy - py*qx); // 直接计算叉积即可
    }

    /// 计算线段与椭圆的交点
    double t1x = (1.0-t1)*px + t1*qx;
    double t1y = (1.0-t1)*py + t1*qy;
    double theta1 = atan2(t1y, t1x);
    double t2x = (1.0-t2)*px + t2*qx;
    double t2y = (1.0-t2)*py + t2*qy;
    double theta2 = atan2(t2y, t2x);

    /// 说明一个交点在PQ反向延长,一个交点在PQ
    if(sgn(t1)<=0&&sgn(t2)>=0&&sgn(t2-1)<=0){
        double ans1 = 0.5 * (px*t2y - t2x*py);
        double ans2 = calAreaOfSectorOfEllipse(theta2, qtheta, a, b);
        return ans1 + ans2;
    }

    /// 说明一个交点在PQ,另一个在PQ延长线上
    if(sgn(t1)>0&&sgn(t1-1)<=0&&sgn(t2-1)>=0){
        double ans1 = calAreaOfSectorOfEllipse(ptheta, theta1, a, b);
        double ans2 = 0.5 * (t1x*qy - t1y*qx);
        return ans1 + ans2;
    }

    /// 两个交点均在PQ内,整个面积由三部分构成
    double ans1 = 0.5 * (t1x*t2y - t2x*t1y);
    double ans2 = calAreaOfSectorOfEllipse(ptheta, theta1, a, b);
    double ans3 = calAreaOfSectorOfEllipse(theta2, qtheta, a, b);
    return ans1 + ans2 + ans3;
}

struct Point64f{
    double x, y;
    Point64f(double xx=0,double yy=0):x(xx),y(yy){}
    double dist2(const Point64f&rhs)const{
        double xx = this->x - rhs.x;
        double yy = this->y - rhs.y;
        return xx*xx + yy*yy;
    }
    double dist(const Point64f&rhs)const{return sqrt(this->dist2(rhs));}
    void disp()const{printf("(%6lf, %6lf)\n",this->x,this->y);}
};

/**
 * 获取旋转矩阵
 * 在原坐标为A,新坐标为B
 * 实际上就是要求OA到OB的旋转角度
 * ans为结果
*/
void getRotationMatrix(const Point64f&A, const Point64f&B, double ans[][2]){
    double theta = atan2(B.y, B.x) - atan2(A.y, A.x);
    ans[0][0] = ans[1][1] = cos(theta);
    ans[0][1] = - (ans[1][0] = sin(theta));
}

/**
 * 给定旋转矩阵,进行旋转
*/
Point64f rotate(const Point64f&p, double const a[][2]){
    return Point64f(a[0][0]*p.x+a[0][1]*p.y, a[1][0]*p.x+a[1][1]*p.y);
}

void solute(){
    Point64f A,B,P[4];
    double L;

    if(!(cin>>A.x>>A.y>>B.x>>B.y>>L)) return;
    for(int i=0;i<3;++i)cin>>P[i].x>>P[i].y;

    /// 确定椭圆方程
    double a = 0.5*L;
    double c = 0.5 * A.dist(B);
    if(c>a){
        puts("0.00");
        return;
    }
    double b = sqrt(a*a-c*c);

    /// 确定椭圆中心
    Point64f O(0.5*(A.x+B.x),0.5*(A.y+B.y));

    /// 首先平移坐标轴,将原点平移到椭圆中心
    A.x -= O.x, A.y -= O.y, B.x -= O.x, B.y -= O.y;
    for(int i=0;i<3;++i)P[i].x-=O.x, P[i].y-=O.y;

    /// 然后考虑旋转
    double rot[2][2];
    getRotationMatrix(A,Point64f(c,0),rot);
    A = rotate(A, rot);
    B = rotate(B, rot);
    for(int i=0;i<3;++i)P[i] = rotate(P[i], rot);

    // A.disp();B.disp();
    // for(int i=0;i<3;++i)P[i].disp();

    /// 然后计算面积
    double ans = 0;
    P[3] = P[0];
    for(int i=0;i<3;++i){
        ans += calAreaOfSegAndEllipse(a,b,P[i].x,P[i].y,P[i+1].x,P[i+1].y);
    }
    printf("%.2lf\n",fabs(ans));
}
int main(){
    // freopen("1.txt","r",stdin);
    solute();
    return 0;
}
  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值