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

*/

posted @ 2019-09-12 18:45  DQS  阅读(388)  评论(10编辑  收藏  举报