ACM-ICPC Asia Beijing Regional Contest 2018 Reproduction hihocoder1870~1879


ACM-ICPC Asia Beijing Regional Contest 2018 Reproduction hihocoder1870~1879

A

簽到,dfs 或者 floyd 都行。

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

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
const int SZ = 1e5 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
	if(flag) n = -n;
	return n;
}

string S1[22],S2[22];
int n;
int f[111][111];

void work() {
    map<string,int> mp;
    int tot = 0;
    for(int i = 1;i <= n;i ++) {
        string s1 = S1[i],s2 = S2[i];
        if(!mp[s1]) mp[s1] = ++ tot;
        if(!mp[s2]) mp[s2] = ++ tot;
    }

    for(int i = 1;i <= tot;i ++) {
        for(int j = 1; j <= tot;j ++) {
            f[i][j] = 0;
        }
    }

    for(int i = 1;i <= n;i ++) {
        string s1 = S1[i],s2 = S2[i];
        int x = mp[s1],y = mp[s2];
        if(f[y][x]) {
            cout << s1 << " " << s2 << endl;
            return ;
        }
        f[x][y] = 1;
        for(int k = 1;k <= tot;k ++) {
            for(int u = 1;u <= tot;u ++) {
                for(int v = 1;v <= tot;v ++) {
                    if(f[u][k] && f[k][v]) {
                        f[u][v] = 1;
                    }
                }
            }
        }
        for(int u = 1;u <= tot;u ++) {
            for(int v = 1;v <= tot;v ++) {
                if(f[u][v]) {
                    if(u == v || f[v][u]) {
                        cout << s1 << " " << s2 << endl;
                        return ;
                    }
                }
            }
        }
       /* for(int u = 1;u <= tot;u ++,puts(""))
            for(int v = 1;v <= tot;v ++)
                printf("%d ",f[u][v]);
        puts("");*/
    }
    puts("0");
    return ;
}

int main() {
    while(~scanf("%d",&n)) {
        for(int i = 1;i <= n;i ++) {
            string s1,s2;
            cin >> s1 >> s2;
            S1[i] = s1;
            S2[i] = s2;
        }
        work();
    }
}

B

閱讀題,模擬,坑點多,WA了好多發,換了個寫法就過了,不知道為什么

C

題意:定義 (a,b,c) 為一組勾股數,c 是斜邊長度,且 (a,b,c) 與 (b,a,c) 看做一組。問 c<=n 的三元組有多少對。 \(n \le 10^9\)

key:推公式

我真的是驚了,為什么要出這種帶了板子就會的題。

首先你要知道勾股數的構造才能做這個題,即 \(a=m^2-n^2,b=2mn,c=m^2+n^2\) 。所有勾股數不好算,所以我們對本原勾股數計數,乘上個倍數即可。在上式中, \((a,b,c)\) 為一組本原勾股數當且僅當 $\gcd(m,n)=1 \text{且 \(m,n\)為一奇一偶}$ 。

\(f(n)\) 為以 n 為斜邊的三元組對數, \(g(n)\) 為以 n 為斜邊的本原勾股數個數,\(F,G\) 為對應的前綴和,那么有:

\[f = g \times 1 \to F=\sum_{1 \le i\le n}G(n/i) \\ G(n) = \sum_{1\le x\le N} \sum_{1 \le y \le N} [x^2+y^2 \le N][\gcd(x,y)=1][\text{x is odd, y is even}] \]

然后就對 G 隨便推一波就行了。形式是一個調和級數。

不預處理的話復雜度是 \(O(Tn^{3/4}\ln n)\),預處理前根號下三分之二次方的復雜度就變成 \(O(Tn^{2/3}\ln n)\) 。預處理就直接用上面給出的這個 G ,隨便積分一下算出來預處理前 B 項的復雜度是 \(O(B \ln B)\) 。實測了一下大概取 \(10^7\) 比較優,十組 \(10^9\) 只跑 0.6s 左右。

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

typedef unsigned long long ULL;
typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
const int SZ = 1e7 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
	if(flag) n = -n;
	return n;
}

int preG[SZ];
const int MAXN = 1e7;
int mu[100100];
bool vis[100100];
int pri[100100];

void preWork(int n) {
    for(int x = 1;x * x <= n;x += 2) {
        for(int y = 2;x * x + y * y <= n;y += 2) {
            if(__gcd(x,y) == 1)
                preG[x*x+y*y] ++;
        }
    }
    for(int i = 1;i <= n;i ++) preG[i] += preG[i-1];

    n = 100000;
    mu[1] = 1;
    for(int i = 2;i <= n;i ++) {
        if(!vis[i]) pri[++ pri[0]] = i,mu[i] = -1;
        for(int j = 1,m;j <= pri[0] && (m=i*pri[j]) <= n;j ++) {
            vis[m] = 1;
            if(i%pri[j] == 0) {
                mu[m] = 0;
            }
            else {
                mu[m] = -mu[i];
            }
        }
    }
}

LL G(int n) {
    if(n <= MAXN) return preG[n];
    LL ans = 0;
    for(int d = 1;d * d * 2 <= n;d ++) {
        int tmp = 0;
        int d2 = d*d,m = n/d2;
        for(int x = 1,lim = sqrt(n-d*d)/d;x*x <= m;x ++) {
            while(lim*lim+x*x > m) lim --;
            if((d*x)&1) tmp += lim / 2;
            else tmp += lim;
        }
        ans += mu[d] * tmp;
    }
    return ans / 2;
}

int main() {
   // freopen("C.in","r",stdin);
    preWork(MAXN);
    int T = read();
    while(T --) {
        int n = read();
        LL ans = 0;
        for(int i = 1,r;i <= n;i = r + 1) {
            r = n / (n / i);
            ans += G(n / i) * (r-i+1);
        }
        printf("%lld\n",ans);
    }
}
/**
3080075432
*/

D

題意:你要從 0 跳到 200,第 i 個位置能跳到第 i+1 和 i+2 上。你可以設置若干個傳送門,一個位置只有一個入口,出口任意。跳到入口處就立馬被傳送到出口,如果構成環則永遠出不去。求使得走到 200 的方案數恰好為 M 的一組傳送門設置方案。 \(M < 2^{32}\)

key:構造

如果你想 ban 掉一個位置,那么一定是原地傳送最優。考慮 ban 掉第 i 個位置,那么設第 i-1 個位置的方案數是 x,那么從第 i-1 開始的方案數是 \(x,0,x,x,2x\) 。這樣就容易想到二進制。

如果想湊出 \(2^i\) ,那么應該設置為 \(2^i,X,2^i,2^i,2^{i+1},Y\) 。如果不想,那么應該是 \(2^i,2^i,2^{i+1},Y\) 。其中 X 是傳到終點,Y 是原地傳送。

#include<bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
using namespace std;
ll m;
ll f[304];
vector<pa>ans;
int w33ha(){
    ans.clear();
    memset(f,0,sizeof(f));
    f[0]=1;
    f[1]=1;
    //int a=0;
    int B = 0;
    for(ll i=0;i<=32;i++){
        if((m&(1LL<<i))){
            ans.push_back({B+1,199});
            ans.push_back({B+5,B+5});
            B += 6;
        }
        else{
            ans.push_back({B+3,B+3});
            B += 4;
        }
    }
    ans.push_back({197,197});
    ans.push_back({198,198});
    printf("%d\n",ans.size());
    for(int i=0;i<ans.size();i++){
        printf("%d %d\n",ans[i].first,ans[i].second);
    }
    return 0;
}

int main(){
    while(scanf("%lld",&m)!=EOF)w33ha();
    return 0;
}

E

F

題意:給一個有向圖,定義 (u,v) 合法當且僅當 u 能走到 v,權值為 u^v。Q 次詢問每次問第 k 大的權值。 \(n \le 5*10^4,m \le 2*10^5 , Q \le 10,T \le 3, k \le 10^9\)

key:bitset

容易想到二分答案,問題在於 check 。如果我們處理出來點 x 能走到哪些點,設這個集合是 S,那么就要找有多少個 \(y \in S\) ,使得 x^y>mid。這個是在 trie 上做的。所以就不用二分答案了,直接在 trie 上貪心。

考慮從 trie 上走的過程:貪心選 0,如果不行,那么走 1。判斷是否不行是用子樹和,這就是值域上的區間和,所以現在問題變成查詢標號小於等於一個數的個數。

所以有一個 bitset 的做法。用 tarjan+拓撲排序 預處理 bitset 的復雜度是 \(O((n+m)n/W)\),模擬 trie 上的復雜度是 \(O(n^2/W\log n)\) ,后者應該是不行的。

所以手寫 bitset ,對 n/W 位建一個前綴和。后者的復雜度為 \(O(n \log n)\) ,需要用到 __builtin_popcountll 。

所以這個題的總復雜度為 \(O(T((n+m)n/W+Qn\log n))\) ,空間復雜度為 \(O(n^2/W)\)

(這里手寫了 bitset ,由於差分的性質所以改成前綴和,由於 __builtin_popcountll 而優化了查詢的復雜度。如果不是二進制 1 的個數那么查詢的復雜度要再乘一個 W,如果不滿足差分性質可以在 bitset 上分塊)

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

typedef unsigned long long ULL;
typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
const int SZ = 5e4 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
	if(flag) n = -n;
	return n;
}

struct Bitset {
    #define W (64)

    int n;
    ULL bits[SZ / W + 10];
    int num[SZ / W + 10];

    void preWork() {
        for(int i = 0;i <= n/W;i ++) num[i] = __builtin_popcountll(bits[i]);
        for(int i = n/W-1;i >= 0;i --) num[i] += num[i+1];
     //  for(int i = 0;i < m;i ++) printf("%d ",sum[i]); puts("");
      //  for(int i = 0;i <= n/W;i ++) printf("%llu ",bits[i]); puts("");
    }

    int ask(int x) {
        if(x > n) return 0;
        int blockid = x / W;
        int ans = __builtin_popcountll(bits[blockid]>>(x%W));
        blockid ++;
        if(blockid <= n/W) ans += num[blockid];
        return ans;
    }

    int ask(int l,int r) {
        return ask(l) - ask(r+1);
    }

    void Or(const Bitset &t) {
        for(int i = 0;i <= n / W;i ++) bits[i] |= t.bits[i];
    }

    void Copy(const Bitset &t) {
        n = t.n;
        for(int i = 0;i <= n / W;i ++) bits[i] = t.bits[i];
    }

    void Set(int x) {
        bits[x/W] |= 1llu << (x%W);
    }

    void init(int nn) {
        n = nn; //n ++;
        for(int i = 0;i <= n / W;i ++) bits[i] = 0;
    }

    void print() {
        for(int i = 0;i < n;i ++) {
            if(bits[i/W] >> (i%W) & 1)
                printf("%d ",i);
        }
        puts("");
    }

    #undef W
}bs[SZ];

struct Tarjan {

    int n;
    int dfn[SZ],low[SZ],dfs_clock,scccnt,sccnum[SZ];
    vector<int> g[SZ],sccnodes[SZ];
    stack<int> S;

    void dfs(int u) {
        dfn[u] = low[u]= ++ dfs_clock;
        S.push(u);
        for(int v : g[u]) {
            if(!dfn[v]) {
                dfs(v);
                low[u] = min(low[u],low[v]);
            }
            else if(!sccnum[v])
                low[u] = min(low[u],dfn[v]);
        }
        if(low[u] == dfn[u]) {
            scccnt ++;
            while(1) {
                int x = S.top(); S.pop();
                sccnum[x] = scccnt;
                sccnodes[scccnt].push_back(x);
                if(x == u) break;
            }
        }
    }

    vector<int> g2[SZ];
    int cd[SZ];

    void work(Bitset bs[]) {
        for(int i = 1;i <= n;i ++)
            if(!dfn[i])
                dfs(i);
      //  for(int u = 1;u <= n;u ++) printf("%d ",sccnum[u]); puts("");
        for(int u = 1;u <= n;u ++)
            for(int v : g[u])
                if(sccnum[u] != sccnum[v]) {
                    g2[sccnum[v]].push_back(sccnum[u]);
                    cd[sccnum[u]] ++;
                }
        static Bitset tmp[SZ];
        for(int i = 1;i <= scccnt;i ++) {
            tmp[i].init(n); //tmp[i].print();
            for(int x : sccnodes[i])
                tmp[i].Set(x);
        }
        queue<int> q;
        for(int i = 1;i <= scccnt;i ++)
            if(cd[i] == 0) {
                q.push(i);
            }
        while(q.size()) {
            int v = q.front(); q.pop();
           // printf("%d: ",v); tmp[v].print();
            for(int x : sccnodes[v]) bs[x].Copy(tmp[v]);
            for(int u : g2[v]) {
                tmp[u].Or(tmp[v]);
                cd[u] --;
                if(cd[u] == 0) {
                    q.push(u);
                }
            }
        }
    }

    void addEdge(int x,int y) {
        g[x].push_back(y);
    }

    void init(int nn) {
        n = nn;
        for(int i = 1;i <= scccnt;i ++) {
            g2[i].clear();
            sccnodes[i].clear();
            cd[i] = 0;
        }
        for(int i = 1;i <= n;i ++) {
            g[i].clear();
            dfn[i] = low[i] = sccnum[i] = 0;
        }
        scccnt = 0;
        dfs_clock = 0;
    }

}tarjan;

int now[SZ];

void work(int n,int &mid,int id,int &k) {
    LL ans = 0;
    for(int i = 1;i <= n;i ++) {
        int t,l,r;
        if(i>>id&1)
            t = bs[i].ask(l=now[i],r=now[i]+(1<<id)-1);
        else
            t = bs[i].ask(l=now[i]+(1<<id),r=now[i]+(2<<id)-1);
        ans += t;
     //   printf("%d: [%d,%d] %d\n",i,l,r,t);
    }
    //cout << id << " " << ans << " " << k << endl;
    if(k <= ans) {
        for(int i = 1;i <= n;i ++) {
            if((i>>id&1) == 0) {
                now[i] |= 1 << id;
            }
        }
        mid |= 1 << id;
    }
    else {
        k -= ans;
        for(int i = 1;i <= n;i ++) {
            if(i>>id&1) {
                now[i] |= 1 << id;
            }
         }
    }
    //cout << k << endl;
   // for(int i = 1;i <= n;i ++) printf("%d ",now[i]); puts("");
}

int main() {
   // freopen("F.in","r",stdin); freopen("my.out","w",stdout);
    int T = read();
    while(T --) {
        int n = read(),m = read(),Q = read();
        tarjan.init(n);
        for(int i = 1;i <= m;i ++) {
            int x = read(),y = read();
            tarjan.addEdge(x,y);
        }
        tarjan.work(bs);
//        for(int i = 1;i <= n;i ++) bs[i].print();
        for(int i = 1;i <= n;i ++){
            bs[i].preWork();
        }
        while(Q --) {
            int k = read();
            int ans = 0;
            for(int i = 1;i <= n;i ++) now[i] = 0;
            for(int i = 16;i >= 0;i --) {
                work(n,ans,i,k);
            }
            printf("%d\n",ans);
        }
    }
}

G

題意:給一個 n 次多項式 F,求一個 n 階多項式 G,使得 G 的每一個根(不一定實根)為 F 的對應根的 m 次冪。 \(n+m \le 10,|a_i| \le 120\),保證 G 的系數 \(< 10^{12}\)

key:牛頓恆等式

設多項式 \(F(x)=\sum_{0\le i \le n} a_ix^i\) ,設其所有根為 \(x_1,x_2...x_n\) (包括復根) ,設 \(S_k=\sum_{i=1}^nx_i^k\)\(b_i=a_{n-i}\) 。則對於任意的正整數 k ,有

\[\sum_{i=1}^kS_ib_{k-i}+k\times b_k=0 \]

所以只要用系數遞推出 n*m 個 S,然后反推出答案即可。因為 \(SG_i=SF_{i \cdot m}\)

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

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
const int SZ = 1e5 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
	if(flag) n = -n;
	return n;
}

LL a[111],b[111],s[111];

int main() {
    int n,m;
    while(~scanf("%d%d",&n,&m) && n && m) {
        memset(a,0,sizeof a);
        memset(b,0,sizeof b);
        memset(s,0,sizeof s);
        for(int i = 0;i < n;i ++) a[i] = read(); a[n] = 1;
        reverse(a,a+n+1);
        for(int k = 1;k <= n*m;k ++) {
            s[k] = -k*a[k];
            for(int i = 1;i < k;i ++) s[k] -= s[i] * a[k-i];
        }
        b[0] = 1;
        for(int k = 1;k <= n;k ++) {
            b[k] = 0;
            for(int i = 1;i <= k;i ++) b[k] -= s[i*m] * b[k-i];
            b[k] /= k;
        }
        for(int i = n;i >= 1;i --) {
            printf("%lld%c",b[i],i==1?'\n':' ');
        }
    }
}

H

題意:定義字符串 A 幾乎匹配 B 當且僅當 B 存在一個子串,與 A 長度相同且至多一個字符不同。給出 A,求有多少個長度為 m 的 B。字符集是 {0,1}。 \(|A|,m\le 40\)

key:dp

主要怕算重,這個只需要找到第一次匹配的位置即可。\(f_{i,j}\) 表示 A 第一次出現位置在 \([i-|A|+1,i]\) 且第 j 位不同的方案數,有:

\[f_{i,j} = 2^{i-|A|}-\sum_{k\le i-|A|,l}f_{k,l}\times 2^{i-|A|-k}-\sum_{i-|A|< k< i}f_{k,l}\times w(i,j,k,l) \]

即前面隨便填的,減去出現過的。第一項是出現位置與當前串不相交的個數,第二項是相交的個數。注意這里相交可能不合法,所以需要一個 \(w(i,j,k,l)\) 來 check,合法時返回 1,否則返回 0。這個可以預處理一下。總復雜度是 \(O(m^2|A|^2)\)

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

typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
const int SZ = 1e5 + 10;
const int INF = 1e9 + 10;
const int mod = 1e9 + 7;
const LD eps = 1e-8;

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
	if(flag) n = -n;
	return n;
}

char s[55];
int n,m;
LL f[55][55];
LL g[55][55][55];

bool isequ(int len,int p1,int p2) {
    string s1,s2;
    for(int i = 1;i <= len;i ++) {
        if(i==p1) {
            if(s[i] == '0') s1 += '1';
            else s1 += '0';
        }
        else s1 += s[i];
    }
    for(int i = n-len+1;i <= n;i ++) {
        if(i==p2) {
            if(s[i] == '0') s2 += '1';
            else s2 += '0';
        }
        else s2 += s[i];
    }
    return s1 == s2;
}

int main() {
    int T = read();
    while(T --) {
        n = read(),m = read();
        scanf("%s",s+1);
        for(int j = 0;j <= n;j ++) {
            for(int k = 1;k < n;k ++) {
                for(int l = 0;l <= n;l ++) {
                    if(isequ(k,j,l)) {
                        g[j][k][l] = 1;
                    }
                    else {
                        g[j][k][l] = 0;
                    }
                }
            }
        }
        for(int i = 1;i <= m;i ++) {
            for(int j = 0;j <= n;j ++) {
                if(i-n < 0) {
                    f[i][j] = 0;
                    continue;
                }
                LL ans = 1ll<<(i-n);
                for(int k = 1;k < i;k ++) {
                    for(int l = 0;l <= n;l ++) {
                        if(k <= i-n) {
                            ans -= f[k][l] * (1ll<<(i-n-k));
                        }
                        else {
                            ans -= f[k][l] * g[j][k-i+n][l];
                        }
                    }
                }
                f[i][j] = ans;
            }
        }
        LL ans = 0;
        for(int i = 1;i <= m;i ++) {
            for(int j = 0;j <= n;j ++) {
                ans += f[i][j] * (1ll<<(m-i));
            }
        }
        printf("%lld\n",ans);
    }
}

I

打表找規律

J

題意:二維平面上給出 n 個點,求所有銳角三角形面積之和。 \(n \le 2000\)

key:掃描線

總面積減去直角三角形面積減去鈍角三角形面積。這三個都能在極角排序的序列上用雙指針定位。算面積用前綴和就行。

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

typedef __int128 int128;
typedef long long LL;
typedef long double LD;
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
const int SZ = 1e6 + 10;
const int INF = 1e9 + 10;
const int mod = 998244353;
const LD eps = 1e-14;
const LD PI = acos(-1);

LL read() {
    LL n = 0;
    char a = getchar();
    bool flag = 0;
    while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
    while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
	if(flag) n = -n;
	return n;
}

LL ksm(LL a,LL b) {
    LL ans = 1;
    while(b) {
        if(b&1) ans = a * ans % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

struct Point {
    LL x,y;
    LL vx,vy;
    Point(LL _x=0,LL _y=0,LL _vx=0,LL _vy=0):x(_x),y(_y),vx(_vx),vy(_vy) { }
    Point operator -(Point &o) { return Point(x-o.x,y-o.y); }
    Point operator +(Point &o) { return Point(x+o.x,y+o.y); }
    Point getV() { return Point(vx,vy); }
}a[SZ],b[SZ],sb[SZ];

int128 operator ^(Point &a,Point &o) { return (int128)a.x*o.y-(int128)a.y*o.x; }
int128 operator *(Point &a,Point &o) { return (int128)a.x*o.x+(int128)a.y*o.y; }

int getxx(Point a) {
    if(a.x>0 && a.y>=0) return 1;
    if(a.x<=0 && a.y>0) return 2;
    if(a.x<0 && a.y<=0) return 3;
    if(a.x>=0 && a.y<0) return 4;
}

bool operator <(Point &a,Point &b) {
    if(getxx(a)!=getxx(b)) return getxx(a) < getxx(b);
    return (int128)a.x*b.y>(int128)a.y*b.x;
}

bool isSameAlpha(Point &a,Point &b) {
    if(getxx(a) != getxx(b)) return false;
    return (a^b) == 0;
}

int main() {
  //  freopen("J.in","r",stdin); freopen("2.out","w",stdout);
    int T = read();
    while(T --) {
        int n = read();
        for(int i = 1;i <= n;i ++) {
            a[i].x = read();
            a[i].y = read();
        }

        LL ans = 0;
        LL sjx = 0,zjsjx = 0,djsjx = 0;
        for(int o = 1;o <= n;o ++) {
            int len = 0;
            for(int i = 1;i <= n;i ++) {
                if(i == o) continue;
                b[++ len] = a[i] - a[o];
                b[len].vx = b[len].x % mod;
                b[len].vy = b[len].y % mod;
                b[++ len] = a[o] - a[i];
            }
            sort(b+1,b+1+len);

            int tot = 0;
            for(int i = 1,j = 1;i <= len;i ++) {
                if(i == len || !isSameAlpha(b[i],b[i+1])) {
                    Point ans = b[j]; j++;
                    while(j<=i) {
                        ans.vx = (ans.vx + b[j].vx) % mod;
                        ans.vy = (ans.vy + b[j].vy) % mod;
                        j ++;
                    }
                    b[++ tot] = ans;
                }
            }
            len = tot;
            for(int i = 1;i <= len;i ++) {
                b[i+len].x = b[i].x;
                b[i+len].y = b[i].y;
                b[i+len].vx = b[i].vx;
                b[i+len].vy = b[i].vy;
            }
            len*=2;
            sb[1] = Point(b[1].vx,b[1].vy);
            for(int i = 2;i <= len;i ++) {
                sb[i].x = (sb[i-1].x + b[i].vx) % mod;
                sb[i].y = (sb[i-1].y + b[i].vy) % mod;
            }

          /*  printf("%d\n",o);
            for(int i = 1;i <= len;i ++) {
                printf("(%3lld,%3lld) (%3lld,%3lld)\n",b[i].x,b[i].y,b[i].vx,b[i].vy);
                //printf("(%lld,%lld)\n",b[i].vx,b[i].vy);
            }*/
           // for(int i = 1;i <= len;i ++) printf("(%lld,%lld)\n",sb[i].x,sb[i].y);

            int r3 = 1,r2 = 1;
            LL t1 = 0,t2 = 0,t3 = 0;
            for(int i = 1;i <= len/2;i ++) {
                Point now = b[i].getV();
            //    if(now.x == 0 && now.y == 0) continue;
                while(r2<=len&&b[i]*b[r2]>0) r2++;
                while(r3+1<=len&&(b[i]^b[r3+1])>0) r3++;
                Point b1 = (sb[r3] - sb[i]);
                Point b2 = (sb[r3] - sb[r2 - 1]);
                (t1 += (now ^ b1) % mod) %= mod;
                (t3 += (now ^ b2) % mod) %= mod;
          //      printf("%d %d %d %d %lld\n",i,r1,r2,r3);
            }
          //  printf("-%d %lld %lld %lld\n",o,t1,t2,t3);
            (sjx += t1) %= mod;
            (zjsjx += t2) %= mod;
            (djsjx += t3) %= mod;
        }
        //printf("%lld %lld %lld\n",sjx,zjsjx,djsjx);
        (ans += sjx * ksm(3,mod-2) % mod - zjsjx - djsjx) %= mod;
        ans += mod; ans %= mod;

        printf("%lld\n",ans);
    }
}

/**
5
5 4
2 4
3 5
3 1

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

*/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM