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\) 為對應的前綴和,那么有:
然后就對 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 ,有
所以只要用系數遞推出 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 位不同的方案數,有:
即前面隨便填的,減去出現過的。第一項是出現位置與當前串不相交的個數,第二項是相交的個數。注意這里相交可能不合法,所以需要一個 \(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
*/