2021acm-icpc区域赛(沈阳)总结笔记

3 篇文章 0 订阅

前言

距离沈阳站小半年了,一直没写总结。

今年的第一场 i c p c icpc icpc 区域赛 , 只能说, 心情很复杂。

比赛链接:https://ac.nowcoder.com/acm/contest/24346

补补题先吧。

题目一览

签到题:B, E, F, J

铜牌题:无

银牌题:H, I, L, M

金牌题: G

很难很难的题:A,C,D,K

由于没有传统意义上的铜牌题以及银牌题太多 , 导致牌子的分布比较离谱,罚时很重要。

本场差不多是 4.5 题铜,5.5题银,7.5题金

E.Edward Gaming, the Champion(签到)

题意:

给你一个字符串 s s s,问有几个" e d g n b edgnb edgnb"的子串 。 ( 1 < = l e n < = 2 ∗ 1 0 5 ) (1<=len<=2*10^5) (1<=len<=2105)

思路:

随便吧

F.Encoded Strings I(签到)

题意:

给你一个长度为n的字符串s ,要对s进行解码。

解码的规则是:

当前字符 " s i = ( s i 最 后 一 次 出 现 后 不 同 字 符 的 数 量 + " a " ) s_i = (s_i最后一次出现后不同字符的数量 + "a") si=(si+"a") "。

例如,对于 s s s = “ a b a c d c abacdc abacdc” ,

a a a 最后一次出现后,还有 “ c d c cdc cdc" , 2 2 2个不同字符 , 那么所有的 a a a 被替换成 c c c

问对 s s s 的每个前缀,输出解码后字典序最小的。

思路:

$n = 1000 , O(n^2) $的。

那题目让干啥干啥就行了。

B.Bitwise Exclusive-OR Sequence(数学+图DFS)

哎,就是这个题

题意:

给出 n n n个点 , m m m个关系 ,每个关系是 < u , v , w > <u,v,w> <u,v,w>

意思是 点 u u u 异或 点 v v v 结果是 w w w

要求你给每个点赋值并满足上述所有的关系。

问最小的点权和是多少?

( 1 < = n < = 1 0 5 , 0 < = m < = 2 ∗ 1 0 5 , 0 < = w < 2 30 ) ( 1<=n<=10^5 , 0<=m<=2*10^5 , 0<=w<2^{30}) (1<=n<=105,0<=m<=2105,0<=w<230)

思路:

首先对于二进制数来讲, 异或操作是不会影响不同数位的,所以每个二进制位单独出来看 , 对于每一位,我们都尽可能让 ‘‘ 1 1 1’’ 更少。

然后异或其实就是规定了两个点在这一数位应该相同还是不同。

所以把规定了关系的点连一起,由于‘ 0 0 0’ 的数量和 ‘ 1 1 1’的数量是互补的,每个连通分量跑一遍图就好。复杂度 O ( 32 ∗ m ) O(32*m) O(32m)

这题我们队被卡常了,场上调了一个多钟, T + W A T+WA T+WA 5 5 5发才过 , 直接导致沈阳站凉凉。

我们最后把

#define int long long

去掉就过了。

后来才知道 l o n g l o n g long long longlong 在部分评测机上跑得会比 i n t int int 慢一些 , 大概 1 、 200 m s 1、200ms 1200ms这样 , 所以开 l o n g l o n g long long longlong的时候要谨慎。

其实一个很重要的原因是我们队一直没有注意到卡常的情况, 导致代码本身写得比较冗杂,要不然 l o n g l o n g long long longlong其实也过得去。

但无论如何 ,还是希望少一点卡常题 , 尤其是重要比赛上…

代码:

重写了一遍,这次只有不到200ms了

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
struct E{
    int to;
    int nxt;
    int w;
}e[N<<1];
int n,m;
int head[N],tot;
int one[32];int siz;
int ans[32];
bool a[32][N];
bool vis[N];
void add_edge(int u,int v,int w){
    e[++tot].nxt = head[u];
    e[tot].to = v;
    e[tot].w = w;
    head[u] = tot;
}
bool ok = true;
void dfs(int u,int f){
    if(!ok) return;
    siz++;
    vis[u] = true;
    for(int i=head[u];i;i=e[i].nxt){
        int v = e[i].to;
        if(v==f) continue;
        int w = e[i].w;
        if(vis[v]){
            for(int j=0;j<=30;j++){
                bool flg = w&(1<<j);
                if(a[j][v]!=(a[j][u]^flg)){    /*把相互矛盾的情况判掉*/
                    ok = false;
                    return;
                }
            }
            continue;
        }
        else{
            for(int j=0;j<=30;j++){
                bool flg = w&(1<<j);
                a[j][v] = a[j][u]^flg;
                one[j]+=a[j][v];
            }
            dfs(v,u);
        }
    }
}
int fac[30];
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    fac[0] = 1;
    for(int i=1;i<=30;i++) fac[i] = fac[i-1]*2;
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        add_edge(u,v,w);
        add_edge(v,u,w);
    }
    for(int i=1;i<=n;i++){
        if(vis[i]) continue;
        siz = 0;
        for(int j=0;j<=30;j++) one[j] = 0;
        dfs(i,0);
        for(int j=0;j<=30;j++) ans[j] += min(one[j],siz-one[j]);
    }
    if(!ok){
        cout<<-1<<endl;
        return 0;
    }
    long long sum = 0;
    for(int j=0;j<=30;j++){
        sum = sum + (long long)ans[j]*(long long)fac[j];
    }
    cout<<sum<<endl;
}

J.Luggage Lock(思维+BFS)

题意:

给你一个四位的锁,每一位是0~9,就这样的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cnwMPXMz-1649668173669)(C:\Users\xi2001\AppData\Roaming\Typora\typora-user-images\image-20220406215215116.png)]

每次可以选择一段区间 [ L , R ] [L,R] [L,R] 同时移动一格(上移或下移) , 问经过最少多少次能从状态 a a a 操作到状态 b b b

输入

6
1234 2345
1234 0123
1234 2267
1234 3401
1234 1344
1234 2468

输出

1
1
4
5
1
4
思路

把初态变为 0 0 0 ,终态对应着改一下.

然后问题就变成了从 0000 0000 0000到对应状态至少需要几次,BFS打表。

代码
#include<bits/stdc++.h>
using namespace std;
int to[4];
map<string ,int> mp;
map<string ,bool> vis;
string add(string now,int i,int j){
    for(int x=i;x<=j;x++){
        if(now[x]=='9') now[x] = '0';
        else now[x]++;
    }
    return now;
}
string sub(string now,int i,int j){
    for(int x=i;x<=j;x++){
        if(now[x]=='0') now[x] = '9';
        else now[x]--;
    }
    return now;
}
void bfs(){
    string s = "0000";
    mp[s] = 0,vis[s] = true;
    queue<string> q;
    q.push(s);
    while(!q.empty()){
        string now = q.front();
        q.pop();
        for(int i=0;i<4;i++){
            for(int j=0;j<=i;j++){
                string tmp1 = add(now,j,i);
                string tmp2 = sub(now,j,i);
                mp[tmp1] = (vis[tmp1]== true)?min(mp[tmp1],mp[now]+1):mp[now]+1;
                mp[tmp2] = (vis[tmp2]== true)?min(mp[tmp2],mp[now]+1):mp[now]+1;
                if(!vis[tmp1]) q.push(tmp1),vis[tmp1] = true;
                if(!vis[tmp2]) q.push(tmp2),vis[tmp2] = true;
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    bfs();
    int t;
    cin>>t;
    while(t--){
        string a,b;
        cin>>a>>b;
        string pp;
        for(int i=0;i<4;i++){
            to[i] = (b[i]-a[i]+10)%10,pp.push_back(to[i]+'0');
        }

        cout<<mp[pp]<<endl;
    }
}

H.Line Graph Matching(图论/tarjan)

题意:

给一个带边权无向图

构造一个新图:将原图的点变成边、边变成点 , 其中新图的边权为原图中对应的俩条边权值和

求新图中的最大独立边集 — 在新图中选取不相邻的若干条边,让他们的权值和最大。

3 < = n < = 1 0 5 , n − 1 < = m < = 2 ∗ 1 0 5 3<=n<=10^5 , n-1<=m<=2*10^5 3<=n<=105,n1<=m<=2105

输入

5 6
1 2 1
1 3 2
1 4 3
4 3 4
4 5 5
2 5 6

输出

21

样例长这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2T3ie5mm-1649668173670)(C:\Users\xi2001\AppData\Roaming\Typora\typora-user-images\image-20220407135310858.png)]

思路:

场上写出来了,但是很可惜, 5 5 5题铜首。

首先很关键的一点是,新图我们是建不出来的。

因为如果原图是菊花图, 那么新图就会是完全图 , 存不下 , 所以只能在原图里找对应关系。

然后不难发现, 其实就是在原图中每次选取两条相邻边,要求最大化权值和

· 如果一共有偶数条边,那么我们是一定能取完所有边的

· 如果一共有奇数条边,那我们必须删掉一条边。如果删的边不是桥的话,那原图就变成偶数条边的连通图,显然可以。

考虑桥的情况:

桥可能会将图变成下面两种 :

1.偶图 - 桥 - 偶图 (第一种桥)

2.奇图 - 桥 - 奇图 (第二种桥)

显然情况2我们是不要的,因为情况2还需要从分出来奇图中再次删边,而很显然,最优解肯定只删一条边(反证一下就行)

所以,奇数条边的答案是 “ s u m − 除 去 第 二 种 桥 外 所 有 的 边 的 最 小 边 权 sum - 除去第二种桥外所有的边的最小边权 sum

显然我们只需要判断“桥”两端点的size是奇数还是偶数 , 这个tarjan+dfs就可以处理。

代码:

重新写一下,就当复习tarjan。

(比队友的代码长了一倍是怎么回事)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 6e5+100;
struct E{
    int to;
    int nxt;
    int w;
    bool cut;
}e[N<<1];
int head[N],tot=-1;
void add_edge(int u,int v,int w){
    e[++tot].to = v;
    e[tot].nxt = head[u];
    e[tot].w = w;
    head[u] = tot;
}
int low[N],dfn[N],idx;
int a[N];
int minn = 1e15+7;
struct edge{
    int u;int v;int w;
};
vector<edge> bridge;
void tarjan(int u,int f){
    low[u] = dfn[u] = ++idx;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v = e[i].to;
        if(v==f) continue;
        if(!dfn[v]){
            tarjan(v,u);
            if(low[u]>low[v]) low[u] = low[v];
            if(low[v]>dfn[u]){
                e[i].cut = true;
                e[i^1].cut = true;
                bridge.push_back({u,v,e[i].w});
                a[u]--,a[v]--;
            }
        }
        else if(low[u]>dfn[v]){
            low[u] = dfn[v];
        }
    }
}

bool vis[N];
int col[N];
int point[N];
int se;
int cnt;
void dfs(int u,int f){
    vis[u] = true;
    cnt += a[u];
    col[u] = se;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v = e[i].to;
        if(vis[v]||v==f||e[i].cut) continue;
        dfs(v,u);
    }
}
int sz[N];
vector<pair<int,int>> v[N];
void dfs2(int u,int f){
    sz[u] = point[u];
    for(auto y:v[u]){
        int vv = y.first,ww = y.second;
        if(vv==f) continue;
        dfs2(vv,u);
        if(sz[vv]%2==0){
            minn = min(minn,ww);
        }
        sz[u] += sz[vv];
        sz[u]++;
    }
}

signed main(){
    ios::sync_with_stdio(false);
    memset(head,-1,sizeof head);
    int n,m;
    cin>>n>>m;
    int sum = 0;
    for(int i=1;i<=m;i++){
        int x,y,w;
        cin>>x>>y>>w;
        add_edge(x,y,w);
        add_edge(y,x,w);
        a[x]++,a[y]++;
        sum += w;
    }
    if(m%2==0) cout<<sum<<endl;
    else{
        tarjan(1,0);
        for(int i=1;i<=n;i++){
            if(!vis[i]){
                cnt = 0;
                se++;
                dfs(i,0);
                point[se] = cnt/2;
            }
        }
        for(auto j:bridge){
            int x = col[j.u],y = col[j.v],z = j.w;
            v[x].push_back(make_pair(y,z));
            v[y].push_back(make_pair(x,z));
        }
        dfs2(1,0);
        for(int i=0;i<=tot;i++){
            if(!e[i].cut) minn = min(minn,e[i].w);
        }
        cout<<sum-minn<<endl;
    }
    return 0;
}

L.Perfect Matchings(树形dp+容斥)

题意

给一个 2 n 2n 2n 个点的完全图,从中删去一个 2 n − 1 2n-1 2n1 个点的树,问剩下的图中有几个完美匹配。

完美匹配是指一个由 n n n 条边组成的边集 , 其中任意两条边不共享同一顶点。

2 < = n < = 2000 2<=n<=2000 2<=n<=2000)

思路

考虑容斥 , 令 a n s i ans_i ansi 表示选了 i i i 条树边的方案数。

选了 i i i 条树边, 就是选了 2 i 2i 2i 个点,此时还剩下 ( 2 n − 2 i ) (2n - 2i) (2n2i) 个点。

对于完美匹配 , 我们从这 ( 2 n − 2 i ) (2n - 2i) (2n2i) 个点中,选一半放左边 , 一半放右边,左右任意匹配

方案数为: C 2 n − 2 i n − i ∗ ( n − i ) ! 2 n \frac{C_{2n-2i}^{n-i}*(n-i)!}{2^n} 2nC2n2ini(ni)!

因此 ,answer = ∑ i = 0 n C 2 n − 2 i n − i ∗ ( n − i ) ! 2 n ∗ a n s i ∗ ( − 1 ) i \sum_{i=0}^n \frac{C_{2n-2i}^{n-i}*(n-i)!}{2^n} *ans_i*(-1)^i i=0n2nC2n2ini(ni)!ansi(1)i

对于求 a n s i ans_i ansi , 考虑 d p dp dp:

d p [ N ] [ N ] [ 2 ] dp[N][N][2] dp[N][N][2] 表示 当前在 i i i 号点 , 它的子树选了 j j j 条边参加匹配 , 当前点是否参与匹配。

代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4040;
const int mod = 998244353;
struct E{
    int to;
    int nxt;
}e[N<<1];
int head[N],tot;
int fac[N],inv[N],sz[N],in2[N];
int dp[N][N][2];
int g[N][2];
void add_edge(int u,int v){
    e[++tot].to = v;
    e[tot].nxt = head[u];
    head[u] = tot;
}
int ksm(int a,int b,int p){
    int ans = 1;
    while(b){
        if(b&1) ans = ans*a%p;
        a = a*a%p;
        b >>= 1;
    }
    return ans%p;
}

void pre(){
    int mx = 4000;
    fac[0] = 1,inv[0] = 1,in2[0] = 1;
    in2[1] = ksm(2,mod-2,mod);
    for(int i=1;i<=mx;i++){
        fac[i] = fac[i-1]*i%mod;
        inv[i] = ksm(fac[i],mod-2,mod)%mod;
        in2[i] = in2[i-1]*in2[1]%mod;
    }
}
int C(int n,int m){
    if(m>n) return 0;
    return fac[n]*inv[n-m]%mod*inv[m]%mod;
}
void dfs(int u,int f){
    sz[u] = 1;
    dp[u][0][0] = 1;
    for(int i=head[u];i;i=e[i].nxt){
        int v = e[i].to;
        if(v==f) continue;
        dfs(v,u);
        memset(g,0,sizeof g);
        for(int j=0;j<=sz[u]/2;j++){
            for(int k=0;k<=sz[v]/2;k++){
                g[j+k][0] += dp[u][j][0]*(dp[v][k][0]+dp[v][k][1]);
                g[j+k][1] += dp[u][j][1]*(dp[v][k][0]+dp[v][k][1]);
                g[j+k+1][1] += dp[u][j][0]*dp[v][k][0];
                g[j+k][0] %= mod,g[j+k][1] %= mod,g[j+k+1][1] %= mod;
            }
        }
        sz[u] = sz[u] + sz[v];
        for(int j=0;j<=sz[u]/2+1;j++)
            dp[u][j][0] = g[j][0],dp[u][j][1] = g[j][1];
       
    }

}
signed main(){
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    pre();
    int rt = -1;
    for(int i=1;i<=2*n-1;i++){
        int u,v;
        cin>>u>>v;
        rt = u;
        add_edge(v,u);
        add_edge(u,v);
    }
    dfs(rt,0);
    int sum = 0;
    for(int i=0;i<=n;i++){
        int now = (dp[rt][i][0]+dp[rt][i][1])%mod;
        int res = n-i;
        int ans = now * C(res*2,res)%mod*fac[res]%mod*in2[res]%mod;
        if(i&1)
            sum = (sum - ans%mod+mod)%mod;
        else sum = (sum + ans)%mod;
    }
    cout<<sum<<endl;
}

M.String Problem(字符串/思维+SAM)

题意:

给一个字符串 s s s , 对于它的每个前缀,求其字典序最大的每个子字符串。

( 1 < = s < = 1 0 6 ) (1<=s<=10^6) (1<=s<=106)

输入

potato

输出

1 1
1 2
3 3
3 4
3 5
5 6

输出的是该子字符串的左右端点

思路

首先可以明确的一点是,“最大的子字符串”一定会延申到该字符串的结尾。

那么这道题就有一种很nb的解法 , 用双指针 O ( n ) O(n) O(n)地处理字符信息(至今没看懂)。

考虑后缀自动机 sam…

写博客的时候才发现我已经把sam忘光了,然后回去看了一下午…

样例1的sam:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N4xdacJx-1649668173671)(C:\Users\xi2001\AppData\Roaming\Typora\typora-user-images\image-20220409152122049.png)]

s a m sam sam已经把每个后缀维护在自动机上了,那我们维护一个 p o s [ ] pos[] pos[],代表该节点在原串中的位置

从起始点开始字典序由大到小 d f s dfs dfs , 某个点第一次被访问到的时候对应的路径肯定就是该点最大的后缀。

代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct state {
    int len, link;
    int next[26];
};
const int MAXLEN = 1001000;
state st[MAXLEN<<1];
int sz, last;
void init() {
    st[0].len = 0;
    st[0].link = -1;
    sz++;
    last = 0;
}
int pos[MAXLEN<<1];
int ans[MAXLEN<<1];
void extend(int c,int id) {
    int cur = sz++;
    st[cur].len = st[last].len + 1;
    pos[cur] = id;
    int p = last;
    while (p != -1 && !st[p].next[c]) {
        st[p].next[c] = cur;
        p = st[p].link;
    }
    if (p == -1) {
        st[cur].link = 0;
    } else {
        int q = st[p].next[c];
        if (st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        } else {
            int clone = sz++;
            st[clone].len = st[p].len + 1;
            memcpy(st[clone].next,st[q].next,sizeof st[q].next);
            st[clone].link = st[q].link;
            pos[clone] = pos[q];
            while (p != -1 && st[p].next[c] == q) {
                st[p].next[c] = clone;
                p = st[p].link;
            }
            st[q].link = st[cur].link = clone;
        }
    }
    last = cur;
}
bool vis[MAXLEN<<1];
void dfs(int u,int len){
    vis[u] = true;
    for(int c=25;c>=0;c--){
        if(st[u].next[c]&&!vis[st[u].next[c]]){
            dfs(st[u].next[c],len+1);
        }
    }
    if(!ans[pos[u]]) ans[pos[u]] = pos[u]-len+1;
}
string s;
signed main(){
    cin>>s;
    init();
    for(int i=0;i<s.length();i++){
        extend(s[i]-'a',i+1);
    }
    dfs(0,0);
    for(int i=1;i<=s.length();i++)
        printf("%d %d\n",ans[i],i);
}

I.Linear Fractional Transformation(数学/高斯消元)

题意

给定一个复变函数 f ( z ) = a ∗ z + b c ∗ z + d f(z) = \frac {a*z+b}{c*z+d} f(z)=cz+daz+b , 系数待定, a , b , c , d a,b,c,d a,b,c,d 都是实数。

给出3个数 , $f(z_1) = w_1 , f(z_2) = w_2 , f(z_3) = w_3 $

f ( z 0 ) f(z_0) f(z0)

输入多组样例 t ( t < = 1 0 5 ) t (t<=10^5) t(t<=105)

每组样例前三行为 z i z_i zi 的实部虚部 , f ( z i ) f(z_i) f(zi) 的实部虚部。

最后一行为 z 0 z_0 z0 的实部虚部。

输出为 f ( z 0 ) f(z_0) f(z0) 的实部虚部。

输入

2
-1 0 0 -1
0 1 -1 0
1 0 0 1
0 -1
-1 0 -1 0
0 1 0 -1
1 0 1 0
0 -1

输出

1.000000000000000 0.000000000000000
0.000000000000000 1.000000000000000
思路

有四个待定系数 a , b , c , d a,b,c,d a,b,c,d, 给三个方程肯定是求不出来的。

于是根据 c c c 的取值进行分类讨论,

c = 0 : c = 0: c=0:

f ( z ) = a d ∗ z + b d f(z) = \frac ad*z+\frac bd f(z)=daz+db , 可解。

考虑令 d = ( 1 , 0 ) d = (1,0) d=(1,0) , 即 f ( z ) = a ∗ z + b f(z) = a*z+b f(z)=az+b

带入 ( z 1 , w 1 ) , ( z 2 , w 2 ) (z_1,w_1),(z_2,w_2) (z1,w1),(z2,w2)求出 a , b a,b a,b

( z 3 , w 3 ) (z_3,w_3) (z3,w3) 验根 , 如果有 w 3 = a ∗ z 3 + b w_3 = a*z_3+b w3=az3+b ,则 c = 0 c = 0 c=0 成立,直接带入 z 0 z_{0} z0 求解。

c ≠ 0 c \ne 0 c=0 :

题目里给了一句提示:

 It can be shown that the answer is always unique to the given contraints.

解唯一 ,那么分子分母对应成比例, 考虑令 c = 1 , 求解出来即可。

w = a ∗ z + b z + d w = \frac {a*z+b}{z+d} w=z+daz+b

移项得 : a ∗ z + b − d = w ∗ z a*z+b- d = w*z az+bd=wz

带入 f ( z 1 ) = w 1 , f ( z 2 ) = w 2 , f ( z 3 ) = w 3 f(z_1) = w_1 , f(z_2) = w_2 , f(z_3) = w_3 f(z1)=w1,f(z2)=w2,f(z3)=w3 得:

在这里插入图片描述

注意式子里的 z i , w i z_i,w_i zi,wi 都是复数,板子得相应地改一下。

代码
#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-9;
const int N = 11;
int equ,var; /* 方程数和未知数个数 */
complex<double> a[N][N],x[N];
double ffabs(complex<double> xx){
    return xx.imag()*xx.imag()+xx.real()*xx.real();
}
int Gauss(){
    int i,j,k,col,max_r;
    for(k=0,col=0;k<equ&&col<var;k++,col++){
        max_r = k;
        for(i=k+1;i<equ;i++){
            if(ffabs(a[i][col])> ffabs(a[max_r][col]))
                max_r = i;
        }
        if(ffabs(a[max_r][col])<eps) return 0;
        if(k!=max_r){
            for(j=col;j<var;j++)
                swap(a[k][j],a[max_r][j]);
            swap(x[k],x[max_r]);
        }
        x[k] /= a[k][col];
        for(j=col+1;j<var;j++) a[k][j]/=a[k][col];
        a[k][col] = 1;
        for(i=0;i<equ;i++)
            if(i!=k){
                x[i] -= x[k]*a[i][col];
                for(j=col+1;j<var;j++) a[i][j] -= a[k][j]*a[i][col];
                a[i][col] = 0;
            }
    }
    return 1;
}
complex<double> z[4],w[4];
int main(){
    int t;
    cin>>t;
    while(t--){
        for(int i=1;i<=3;i++){
            double za,zb,wa,wb;
            scanf("%lf %lf %lf %lf",&za,&zb,&wa,&wb);
            z[i].real(za);z[i].imag(zb);
            w[i].real(wa);w[i].imag(wb);
        }
        double za,zb;
        complex<double> ans;
        scanf("%lf %lf",&za,&zb);
        z[0].real(za),z[0].imag(zb);
        complex<double> aa = (w[1]-w[2]) / (z[1]-z[2]);
        complex<double> bb = w[1] - aa*z[1];
        if(ffabs(aa*z[3]+bb-w[3])<eps){
            ans = aa*z[0]+bb;
        }
        else{
            for(int i=0;i<3;i++){
                a[i][0] = z[i+1],a[i][1] = complex<double>(1,0),a[i][2] = -w[i+1];
                x[i] = w[i+1]*z[i+1];
            }
            equ = var = 3;
            Gauss();
            aa = x[0],bb = x[1];
            complex<double> cc = complex<double>(1,0),dd = x[2];
            ans = (aa*z[0]+bb)/(cc*z[0]+dd);
        }
        printf("%.10f %.10f\n",ans.real(),ans.imag());
    }
    return 0;
}
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值