ICPC2023瀋陽站題解(B C D E I J K M)

maple276發表於2024-04-18

本場金牌線為六題前一半,這裡提供難度前八題的題解。

本場真是個細節場,銀牌金牌題細節都相當地多。

ICPC2023瀋陽站

C:

作為簽到題,對這種賽制熟悉不熟悉直接區分了一血時間。諤諤。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define FOR() ll le=e[u].size();for(ll i=0;i<le;i++)
#define QWQ cout<<"QwQ\n";
#define ll long long
#include <vector>
#include <queue>
#include <map>

using namespace std;
const ll N=501010;
const ll qwq=303030;
const ll inf=0x3f3f3f3f;

ll T;
ll n,m;
int du[N];

inline ll read() {
    ll sum = 0, ff = 1; char c = getchar();
    while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); }
    while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
    return sum * ff;
}

int main() {
    int ans;
    n = read(); m = read();
    if(n==0 && m==0) ans = 4;
    if(n==0 && m==1) ans = 4;
    if(n==0 && m==2) ans = 6;
    if(n==1 && m==0) ans = 3;
    if(n==1 && m==1) ans = 3;
    if(n==1 && m==2) ans = 4;
    if(n==2 && m==0) ans = 2;
    if(n==2 && m==1) ans = 2;
    if(n==2 && m==2) ans = 2;
    cout<<ans;
    return 0;
}

J:

博弈論,看著挺嚇人的,但結論很簡單。

手玩一會兒後會發現兩個關鍵點:1、u --> v 後,u會變成葉子;2、u --> v 且 v 是一個葉子,那麼操作完樹的形態是不會改變的,根據題意,我們不允許 v 是葉子。

所以每進行一個操作就會多一個葉子,統計一下非葉結點的奇偶即可。

有個坑點是 n=2,作為簽到題,坑罰時用的,很快就能發現。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define FOR() ll le=e[u].size();for(ll i=0;i<le;i++)
#define QWQ cout<<"QwQ\n";
#define ll long long
#include <vector>
#include <queue>
#include <map>

using namespace std;
const ll N=501010;
const ll qwq=303030;
const ll inf=0x3f3f3f3f;

ll T;
ll n,m;
int du[N];

inline ll read() {
    ll sum = 0, ff = 1; char c = getchar();
    while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); }
    while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
    return sum * ff;
}

int main() {
    int x,y;
    n = read();
    for(int i=1;i<n;i++) {
        x = read(); y = read();
        du[x]++; du[y]++;
    }
    int ji = 0, you = 0;
    for(int i=1;i<=n;i++) {
        if(du[i]>1) ji^=1, you = 1;
    }
    if(!(ji&1) && you) cout<<"Alice";
    else cout<<"Bob";
    return 0;
}

E:

好經典的題意。農夫,狼,羊,小船,過河。

這題不需要數學結論,根據資料範圍容易寫出BFS方程,\(dis[x][y][0/1]\) 表示某岸上羊和狼的數量以及農夫現在的位置,每次過河check一下。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define FOR() ll le=e[u].size();for(ll i=0;i<le;i++)
#define QWQ cout<<"QwQ\n";
#define ll long long
#include <vector>
#include <queue>
#include <map>

using namespace std;
const ll N=501010;
const ll qwq=303030;
const ll inf=0x3f3f3f3f;

ll T;
int X,Y,Q,P;
struct D{
    int di,x,y,cl;
};
queue <D> q;
int dis[111][111][2];

inline ll read() {
    ll sum = 0, ff = 1; char c = getchar();
    while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); }
    while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
    return sum * ff;
}

inline bool check(int x,int y,int cl) {
    if(cl==1) {
        return (Y-y)<=((X-x)+P) || x==X;
    }
    else {
        return y<=(x+P) || x==0;
    }
}

int main() {
    X = read(); Y = read(); Q = read(); P = read();
    memset(dis,0x3f,sizeof(dis));
    dis[0][0][0] = 0; q.push({0,0,0,0});
    while(!q.empty()) {
        D now = q.front(); q.pop();
        // cout<<now.di<<" : "<<now.x<<" "<<now.y<<" "<<now.cl<<"\n";
        if(now.cl==0) {
            for(int i=0;i<=X-now.x;i++) {
                for(int j=0;j<=Y-now.y;j++) {
                    if(j+i>Q) break;
                    if(!check(now.x+i,now.y+j,1)) continue;
                    // cout<<" i = "<<i<<" j = "<<j<<endl;
                    if(now.x+i==X) { cout<<now.di+1; return 0; }
                    if(dis[now.x+i][now.y+j][1] > now.di+1)
                    q.push({now.di+1,now.x+i,now.y+j,1}), dis[now.x+i][now.y+j][1] = now.di+1;
                }
            }
        }
        else {
            for(int i=0;i<=now.x;i++) {
                for(int j=0;j<=now.y;j++) {
                    if(j+i>Q) break;
                    if(!check(now.x-i,now.y-j,0)) continue;
                    if(dis[now.x-i][now.y-j][0] > now.di+1)
                    q.push({now.di+1,now.x-i,now.y-j,0}), dis[now.x-i][now.y-j][0] = now.di+1;
                }
            }
        }
    }
    cout<<-1;
    return 0;
}

K:

資料結構好題。

漲分次數最多肯定是漲分場全放前面,假設有 k 個漲分場,我們把漲分多的場放前面,最後一場漲分最小,如果我們把所有的掉分場插到漲分最小的場前面,就有可能得到 k-1,同理 k-2 就是插在倒數第二大的漲分場前面,答案就是搜尋掉分場加起來的數值可以覆蓋多少加分場。

我擼了棵平衡樹,其實權值線段樹上二分就行。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define FOR() ll le=e[u].size();for(ll i=0;i<le;i++)
#define QWQ cout<<"QwQ\n";
#define ll long long
#include <vector>
#include <queue>
#include <map>
#define ls son[x][0]
#define rs son[x][1]

using namespace std;
const ll N=801010;
const ll qwq=303030;
const ll inf=0x3f3f3f3f3f3f3f3f;

ll Q;
ll n,m,tot;
ll a[N];
ll fu;
ll rt,fa[N],son[N][2],siz[N],num[N],val[N],sum[N];

inline ll read() {
    ll sum1 = 0, ff = 1; char c = getchar();
    while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); }
    while(c>='0'&&c<='9') { sum1 = sum1 * 10 + c - '0'; c = getchar(); }
    return sum1 * ff;
}

inline ll touhou(ll x) { return son[fa[x]][1]==x; }
inline void pushup(ll x) { siz[x] = siz[ls] + siz[rs] + num[x]; sum[x] = sum[ls] + sum[rs] + num[x] * val[x]; }
inline void rotate(ll x) {
    ll y = fa[x], z = fa[y], k = touhou(x), w = son[x][k^1];
    son[z][touhou(y)] = x; son[x][k^1] = y; son[y][k] = w;
    fa[w] = y; fa[y] = x; fa[x] = z; pushup(y); pushup(x);
}
inline void splay(ll x,ll goal) {
    while(fa[x]!=goal) {
        ll y = fa[x], z = fa[y];
        if(z!=goal) {
            if(touhou(y)==touhou(x)) rotate(y);
            else rotate(x);
        }
        rotate(x);
    }
    if(!goal) rt = x;
}

inline void insert(ll v) {
    ll x = rt, f = 0;
    while(val[x]!=v && x)
        f = x, x = son[x][val[x]<v];
    if(x) num[x]++, pushup(x);
    else {
        x = ++tot;
        if(f) son[f][val[f]<v] = x;
        fa[x] = f; siz[x] = num[x] = 1;
        val[x] = sum[x] = v;
    }
    splay(x,0);
}

inline void find(ll v) {
    ll x = rt;
    while(val[x]!=v && son[x][ val[x]<v ])
        x = son[x][ val[x]<v ];
    splay(x,0); 
}
inline ll qian(ll v) {
    find(v);
    if(val[rt]<v) return rt;
    ll x = son[rt][0];
    while(rs) x = rs; return x;
}
inline ll hou(ll v) {
    find(v);
    if(val[rt]>v) return rt;
    ll x = son[rt][1];
    while(ls) x = ls; return x;
}

void del(ll v) {
    ll y = qian(v), x = hou(v);
    splay(y,0); splay(x,y);
    if(num[ls]>1) num[ls]--, siz[ls]--, sum[ls]-=val[ls];
    else ls = 0;
    pushup(x); pushup(y);
}

inline ll query(ll v) {
    if(!fu) return 0;
    ll x = rt;
    while(1) {
        if(sum[ls]>=v) x = ls;
        else if(sum[ls]+val[x]*num[x]<v) v -= sum[ls]+val[x]*num[x], x = rs;
        else {v -= sum[ls]; break;}
    }
    splay(x,0);
    ll res = siz[ls]-1;
    res += v/val[rt];
    return res;
}

int main() {
    ll x,wei;
    insert(inf); insert(0);
    n = read(); Q = read();
    for(ll i=1;i<=n;i++) {
        a[i] = read();
        if(a[i]>0) insert(a[i]);
        else fu -= a[i];
    }
    while(Q--) {
        wei = read(); x = read();
        if(a[wei]<=0) fu += a[wei];
        else del(a[wei]);
        if(x<=0) fu -= x;
        else insert(x);
        a[wei] = x;
        // outing();
        cout<<query(fu)+1<<"\n";
    }
    return 0;
}

M:

手玩題。

起始數字不重要,所以我預設從1開始,發現有些無厘頭。

其實該從0開始,0才是程式設計師的浪漫。手玩過後發現兩位一迴圈,一個迴圈節是 0->1->3->2->0,每多一位就把前面的部分複製 4 遍(要注意純0只會出現一次)。第 k 次到達 0 的步數是 \(4^1+4^2+4^3+...+4^k\) ,其他的數字,到達次數取決於末尾 0 的個數,設題目所給次數為x,x要小於等於0的個數加一。我們可以把前面 1 的步數計算完,再加上 \(4^1+4^2+...+4^x\)

細節還挺多,感覺導致這場金牌線題數少的最大元兇就是這題,但是個不錯的手玩題。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define FOR() ll le=e[u].size();for(ll i=0;i<le;i++)
#define QWQ cout<<"QwQ\n";
#define ll long long
#include <vector>
#include <queue>
#include <map>

using namespace std;
const ll N=2201010;
const ll qwq=303030;
const ll inf=0x3f3f3f3f;
const ll p=1000000007, in3=333333336;

ll T;
ll n,m,k;
char s[N],t[N];
ll a[N],b[N],c[N];
ll ans;
ll f[N];

inline ll read() {
    ll sum = 0, ff = 1; char c = getchar();
    while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); }
    while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
    return sum * ff;
}

inline ll ksm(ll aa,ll bb) {
    ll sum = 1;
    while(bb) {
        if(bb&1) sum = sum * aa %p;
        bb >>= 1; aa = aa * aa %p;
    }
    return sum;
}

void chushihua() {
    ans = 0;
}

inline ll duan(ll i) {
    if(i<=N-10) return f[i];
    return (ksm(4,i)-1+p) * in3 %p;
}

int main() {
    f[1] = 1;
    for(int i=1;i<=N-10;i++) f[i] = (f[i-1] * 4ll + 1ll) %p;
    T = read();
    while(T--) {
        chushihua();
        scanf("%s%s",s+1,t+1); n = strlen(s+1); m = strlen(t+1);
        k = read();
        ll len = max(n,m);
        if(len&1) len++;
        for(ll i=1;i<=len;i++) {
            if(i<=n) a[i] = s[n+1-i] - '0'; else a[i] = 0;
            if(i<=m) b[i] = t[m+1-i] - '0'; else b[i] = 0;
        }
        len >>= 1;
        int you = 0;
        for(ll i=1;i<=len;i++) {
            int yi = a[(i<<1)-1] ^ b[(i<<1)-1];
            int er = a[i<<1] ^ b[i<<1];
            if(yi && er) c[i] = 2;
            if(yi && !er) c[i] = 1;
            if(!yi && er) c[i] = 3;
            if(!yi && !er) c[i] = 0;
            if(c[i] && !you) you = i;
        }
        if(!you) {
            cout<<(duan(k)-1+p)%p<<"\n";
            continue;
        }
        if(k>you) { cout<<"-1\n"; continue; }
        c[k-1] = 4;
        for(ll i=1;i<=len;i++) {
            (ans += c[i] * duan(i) %p) %= p;
        }
        cout<<(ans%p+p)%p<<"\n";
    }
    return 0;
}

B:

金牌題一。

這題我做過一個簡化版本,出題人應該就是根據那一道題改編的。

簡化版的那道題是求數字一上一下的排列數量,可以 n^2 dp 解決,\(f[i][j]\) 表示填寫到第 i 個數字,第 i 個數字在前面這些數字中排名第 j,在轉移時我們不考慮每個數字的絕對數值,而是考慮它們的相對數值,也就是列舉下一個數字插在哪個位置。

這題資料範圍很友好,n^4 甚至 n^5 都能過,為了求字典序第 k 大,我們就從第一位填1開始,每次計算下剩餘數位有多少方案,每一位掃一遍,n^2可以掃完。怎麼統計剩餘數位的方案呢,我們把數值看成橫軸,位置看成縱軸,我們已經確定了前幾個位置的數值,那麼橫軸上留下一些剩餘數值供我們填寫,這些數值就構成一上一下的關係,我們用我們上面的思路可以輕鬆地求出。

思路比較流程,但是細節比較多,比如橫軸上留下數值的位置也是有限制的,建議自己手玩一下。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define FOR() ll le=e[u].size();for(ll i=0;i<le;i++)
#define QWQ cout<<"QwQ\n";
#define ll long long
#include <vector>
#include <queue>
#include <map>

using namespace std;
const ll N=201010;
const ll qwq=303030;
const ll inf=1e18;
const ll p=1000000007, in3=333333336;

ll n,K;
ll f[111][111];
ll dp[111][111];
ll da = 50;
ll yi;
ll ans[111], a[111];

inline ll read() {
    ll sum = 0, ff = 1; char c = getchar();
    while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); }
    while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
    return sum * ff;
}

void qiu() {
    for(int i=0;i<=da;i++) f[i][0] = 1;
    for(int i=0;i<=da;i++) {
        memset(dp,0,sizeof(dp));
        for(int k=1;k<=i+1;k++) dp[1][k] = 1;
        f[i][1] = i+1;
        for(int j=2;j<=da;j++) {
            if(j&1) {
                for(int k=2;k<=i+j;k++) {
                    for(int l=1;l<=k-1;l++) {
                        dp[j][k] += dp[j-1][l];
                        if(dp[j][k] > inf) { dp[j][k] = inf; break; }
                    }
                }
            }
            else {
                for(int k=1;k<i+j;k++) {
                    for(int l=k;l<i+j;l++) {
                        dp[j][k] += dp[j-1][l];
                        if(dp[j][k] > inf) { dp[j][k] = inf; break; }
                    }
                }
            }
            for(int k=1;k<=i+j;k++) {
                f[i][j] += dp[j][k];
                if(f[i][j] > inf) { f[i][j] = inf; break; }
            }
        }
    }
}

ll check() {
    ll res = 1;
    ll len = 0, sum = 0;
    for(int j=1;j<=n;j++) {
        if(!a[j]) len++;
        if(j==n || a[j+1]) {
            if(inf / res < f[sum][len]) { res = inf; break; }
            res *= f[sum][len];
            sum += len;
            len = 0;
        }
    }
    return res;
}

int main() {
    qiu();
    n = read(); K = read();
    ll now = 0;
    for(int j=1;j<=n;j++) {
        if((K - now - 1) / f[0][j-1] < f[j-1][n-j]) { yi = j; break; }
        now += f[0][j-1] * f[j-1][n-j];
    }
    if(!yi) { cout<<-1; return 0; }
    ans[1] = yi;
    a[yi] = 1;
    for(int i=2;i<=n;i++) {
        for(int j=1;j<=n;j++) {
            bool ke = 0;
            if(j==1 && a[2]) ke = 1;
            if(j==n && a[n-1]) ke = 1;
            if(a[j-1] && a[j+1]) ke = 1;
            if((j&1) == (yi&1)) ke = 1;
            if(a[j]) ke = 0;
            if(!ke) continue;
            a[j] = i;
            if(now + check() >= K) { ans[i] = j; break; }
            now += check();
            a[j] = 0;
        }
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
    return 0;
}

D:

金牌題二。

S中選一段字串,T中選一段字串,拼起來要求是一個平方串。

資料範圍是 n^2,於是開始思考各種列舉,我們設 S 中選出的串為 ABA 的形式,T中選出串為 B 的形式(T長則反過來再計算一次就行)。

我們 n^2 的列舉位置,一個是第一個 A 的左端 \(l1\),一個是第二個 A 的左端 \(l2\),這兩個位置的字尾串的LCP,決定了 B 串的最小要求長度。統計有多少滿足的 B 串可以先 n^2 預處理 S 中每一個位置和 T 中每一個位置的 LCS(在S的每個位置上記錄各個LCS數值的出現次數),統計答案就用 \(l2\) 這個位置記錄的資料,計算大於 B 串最小長度要求的串的貢獻和(經典的兩個字首和統計三角面積)。

感覺,還是有很多細節。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define FOR() ll le=e[u].size();for(ll i=0;i<le;i++)
#define QWQ cout<<"QwQ\n";
#define ll long long
#include <vector>
#include <queue>
#include <map>

using namespace std;
const ll N=201010;
const ll qwq=303030;

ll n,m;
char s[5050],t[5050],z[5050];
int f[5050][5050],g[5050][5050];
ll a[5050],b[5050];
ll ans;

inline ll read() {
    ll sum = 0, ff = 1; char c = getchar();
    while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); }
    while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
    return sum * ff;
}

void solve(ll cl) {
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    for(ll i=n;i>=1;i--) {
        for(ll j=n;j>=1;j--) {
            if(s[i]==s[j]) f[i][j] = f[i+1][j+1] + 1;
        }
    }
    for(ll i=1;i<=n;i++) {
        for(ll j=1;j<=m;j++) {
            if(s[i]==t[j]) g[i][j] = g[i-1][j-1] + 1;
        }
    }
    for(ll i=1;i<=n;i++) {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(ll j=1;j<=m;j++) {
            a[ g[i][j] ]++;
            b[ g[i][j] ]+=g[i][j];
        }
        for(ll j=m;j>=1;j--) a[j] += a[j+1], b[j] += b[j+1];
        for(ll j=1;j<=i;j++) {
            ll wo = f[i+1][j];
            ll zong = (i-j+1);
            ll L = max(1ll,zong-wo), R = (cl==1) ? zong : zong-1;
            if(L>R) continue;
            ans += a[R+1] * (R-L+1);
            ans += (b[L] - b[R+1]) - (a[L] - a[R+1]) * (L-1);
        }
    }
}

int main() {
    scanf("%s%s",s+1,t+1); n = strlen(s+1); m = strlen(t+1);
    solve(1);
    reverse(s+1,s+n+1);
    reverse(t+1,t+m+1);
    for(ll i=1;i<=n;i++) z[i] = s[i];
    for(ll i=1;i<=m;i++) s[i] = t[i];
    for(ll i=1;i<=n;i++) t[i] = z[i];
    swap(m,n);
    solve(0);
    cout<<ans;
    return 0;
}

I:

金牌題三。

其實不是很難,分類討論也不是說逆天地多,這題能成為金牌題和榜歪有一定的關係。

首先這題一個很關鍵的點:肯定有一個矩形能夠覆蓋兩個角。然後這題就能順利進行下去了。

討論時我們預設覆蓋行的矩形比覆蓋列的矩形多,否則swap所有資料,可以少一半的碼量。

然後是討論:

1、有三個覆蓋行的矩形:就類似於三個橫扛上下滑動。因為要覆蓋四個角所以至少有兩個橫槓是需要固定在角上的,所以就只有一個橫槓能上下滑(注意有三個槓都跑到角落的情況,需要容斥)。這個其實是最複雜最難算的情況。

2、有兩個覆蓋行的矩形:兩個橫槓上下滑,一個需要固定在上面,一個需要固定在下面。如果中間還有縫隙的話,剩餘一個沒有覆蓋行的矩形肯定是做不到的;如果中間沒有縫隙,剩餘一個矩形位置隨意。

3、一個覆蓋行的矩形,一個覆蓋列的矩形:無論它倆滑到上下左右的任意位置,留下的空都是固定的,而且留下了一個角導致剩餘的矩形位置也固定了,答案不是0就是4。

4、一個覆蓋行的矩形:除了這個槓需要覆蓋兩個角以外,剩下的兩個矩形也需要各覆蓋一個角,答案不是0就是4。

5、沒有覆蓋行或列的矩形:答案是0。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define FOR() ll le=e[u].size();for(ll i=0;i<le;i++)
#define QWQ cout<<"QwQ\n";
#define ll long long
#include <vector>
#include <queue>
#include <map>

using namespace std;
const ll N=201010;
const ll qwq=303030;
const ll p=1000000007;

ll T;
ll n,m,H,W;
ll a1,b1,a2,b2,a3,b3;

inline ll read() {
    ll sum = 0, ff = 1; char c = getchar();
    while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); }
    while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
    return sum * ff;
}

void solve1() {
         if(a1*b1==n*m) cout<<(n-a2+1) * (m-b2+1) %p * (n-a3+1) %p * (m-b3+1) %p<<"\n";
    else if(a2*b2==n*m) cout<<(n-a1+1) * (m-b1+1) %p * (n-a3+1) %p * (m-b3+1) %p<<"\n";
    else if(a3*b3==n*m) cout<<(n-a1+1) * (m-b1+1) %p * (n-a2+1) %p * (m-b2+1) %p<<"\n";
}

void solve2() {
    if(W==3) { swap(n,m); swap(a1,b1); swap(a2,b2); swap(a3,b3); }
    ll res = 0;
    if(b1+b2+b3<m) { cout<<"0\n"; return ; }

    if(b1+max(b2,b3)>=m) res += 2;
    if(b2+max(b1,b3)>=m) res += 2;
    if(b3+max(b1,b2)>=m) res += 2;

    if(b1+b2>=m) res += 2 * (m-b3-1);
    else {
        ll L = max(2ll,m-b2-b3+1);
        ll R = min(m-b3,b1+1);
        if(L<=R) res += 2 * (R-L+1);
    }
    if(b1+b3>=m) res += 2 * (m-b2-1);
    else {
        ll L = max(2ll,m-b3-b2+1);
        ll R = min(m-b2,b1+1);
        if(L<=R) res += 2 * (R-L+1);
    }
    if(b2+b3>=m) res += 2 * (m-b1-1);
    else {
        ll L = max(2ll,m-b3-b1+1);
        ll R = min(m-b1,b2+1);
        if(L<=R) res += 2 * (R-L+1);
    }
    cout<<(res%p+p)%p<<"\n";
}

void solve3() {
    if(W==2) { swap(n,m); swap(a1,b1); swap(a2,b2); swap(a3,b3); }
    ll A, B, C, D;
    if(a3!=n) A = b1, B = b2, C = a3, D = b3;
    if(a2!=n) A = b1, B = b3, C = a2, D = b2;
    if(a1!=n) A = b2, B = b3, C = a1, D = b1;
    if(A+B<m) { cout<<"0\n"; return ; }
    cout<<2*(n-C+1)%p*(m-D+1)%p<<"\n";
}

void solve4() {
    if((a1+a2+a3>=2*n) && (b1+b2+b3>=2*m)) cout<<"4\n";
    else cout<<"0\n";
}

void solve5() {
    if(W==1) { swap(n,m); swap(a1,b1); swap(a2,b2); swap(a3,b3); }
    if(a2==n) swap(a1,a2), swap(b1,b2);
    if(a3==n) swap(a1,a3), swap(b1,b3);
    if(b1+b2<m || b1+b3<m) { cout<<"0\n"; return ; }
    if(a2+a3<n) { cout<<"0\n"; return ; }
    cout<<"4\n";
}

int main() {
    T = read();
    while(T--) {
        n = read(); m = read();
        a1 = read(); b1 = read(); a2 = read(); b2 = read(); a3 = read(); b3 = read();
        if(a1*b1==n*m || a2*b2==n*m || a3*b3==n*m) { solve1(); continue; }
        H = W = 0;
        if(a1==n) H++; if(a2==n) H++; if(a3==n) H++;
        if(b1==m) W++; if(b2==m) W++; if(b3==m) W++;
        if(!H && !W) { cout<<"0\n"; continue; }
        if(H==3 || W==3) { solve2(); continue; }
        if(H==2 || W==2) { solve3(); continue; }
        if(H==1 && W==1) { solve4(); continue; }
        if(H+W==1) { solve5(); continue; }
    }
    return 0;
}

相關文章