• 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 ext{且 m,n 为一奇一偶})

    (f(n)) 为以 n 为斜边的三元组对数, (g(n)) 为以 n 为斜边的本原勾股数个数,(F,G) 为对应的前缀和,那么有:

    [f = g imes 1 o F=sum_{1 le ile n}G(n/i) \ G(n) = sum_{1le xle N} sum_{1 le y le N} [x^2+y^2 le N][gcd(x,y)=1][ ext{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
    ",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
    ",ans.size());
        for(int i=0;i<ans.size();i++){
            printf("%d %d
    ",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/Wlog n)) ,后者应该是不行的。

    所以手写 bitset ,对 n/W 位建一个前缀和。后者的复杂度为 (O(n log n)) ,需要用到 __builtin_popcountll 。

    所以这个题的总复杂度为 (O(T((n+m)n/W+Qnlog 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
    ",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
    ",ans);
            }
        }
    }
    

    G

    题意:给一个 n 次多项式 F,求一个 n 阶多项式 G,使得 G 的每一个根(不一定实根)为 F 的对应根的 m 次幂。 (n+m le 10,|a_i| le 120),保证 G 的系数 (< 10^{12})

    key:牛顿恒等式

    设多项式 (F(x)=sum_{0le 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 imes 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?'
    ':' ');
            }
        }
    }
    
    

    H

    题意:定义字符串 A 几乎匹配 B 当且仅当 B 存在一个子串,与 A 长度相同且至多一个字符不同。给出 A,求有多少个长度为 m 的 B。字符集是 {0,1}。 (|A|,mle 40)

    key:dp

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

    [f_{i,j} = 2^{i-|A|}-sum_{kle i-|A|,l}f_{k,l} imes 2^{i-|A|-k}-sum_{i-|A|< k< i}f_{k,l} imes 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
    ",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
    ",o);
                for(int i = 1;i <= len;i ++) {
                    printf("(%3lld,%3lld) (%3lld,%3lld)
    ",b[i].x,b[i].y,b[i].vx,b[i].vy);
                    //printf("(%lld,%lld)
    ",b[i].vx,b[i].vy);
                }*/
               // for(int i = 1;i <= len;i ++) printf("(%lld,%lld)
    ",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
    ",i,r1,r2,r3);
                }
              //  printf("-%d %lld %lld %lld
    ",o,t1,t2,t3);
                (sjx += t1) %= mod;
                (zjsjx += t2) %= mod;
                (djsjx += t3) %= mod;
            }
            //printf("%lld %lld %lld
    ",sjx,zjsjx,djsjx);
            (ans += sjx * ksm(3,mod-2) % mod - zjsjx - djsjx) %= mod;
            ans += mod; ans %= mod;
    
            printf("%lld
    ",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
    
    */
    
    
  • 相关阅读:
    简单取色器
    第一部分实现功能:使用一个TabControl和一个Memo和TDictionary类实现文本临时存储
    功能设计笔记概要
    NFC与RFID简介
    【JAVA设计模式-第四课】观察者模式-屌丝求职记+新闻订阅
    「学习笔记」左偏树
    「SP122」STEVE
    「学习笔记」 FHQ Treap
    「整理」同余
    「目录」数论
  • 原文地址:https://www.cnblogs.com/dqsssss/p/11514612.html
Copyright © 2020-2023  润新知