文件源和模板
目录
基本
文件头
// Author: kyEEcccccc
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
#define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max((a), (b)))
#define MIN(a, b) ((a) = min((a), (b)))
#define SZ(a) ((int)((a).size()) - 1)
signed main(void)
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(nullptr);
return 0;
}
fread/fwrite快读快写
namespace IO
{
constexpr int BUF = 1000000;
char ibuf[BUF];
int ilen = 0, icur = 0;
int rc(void)
{
if (icur == ilen)
{
icur = 0;
ilen = fread(ibuf, sizeof (char), BUF, stdin);
if (icur == ilen) return -1;
}
return ibuf[icur++];
}
LL rll(void)
{
LL x = 0, s = 1, c = rc();
while (c < '0' || c > '9')
{
if (c == -1) return 0;
s = c == '-' ? -1 : 1;
c = rc();
}
do {
x = (x << 1) + (x << 3) + c - '0';
c = rc();
} while (c >= '0' && c <= '9');
return x * s;
}
char obuf[BUF], ostk[20];
int olen = 0, otop;
void flush(void)
{
fwrite(obuf, sizeof (char), olen, stdout);
olen = 0;
}
void wc(char c)
{
if (olen == BUF) flush();
obuf[olen++] = c;
}
void wll(LL x)
{
if (x == 0) return wc('0');
if (x < 0) x = -x, wc('-');
otop = 0;
while (x) ostk[++otop] = x % 10 + '0', x /= 10;
while (otop) wc(ostk[otop--]);
}
}
自动对拍
// Author: kyEEcccccc
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
#define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max(a, b))
#define MIN(a, b) ((a) = min(a, b))
#define SZ(a) ((int)((a).size()) - 1)
const string NAME = "b";
const string GEN = ".\\" + NAME + "_gen", STD = ".\\" + NAME + "_std", SOL = ".\\" + NAME;
const string DIFF = "fc.exe " + NAME + ".out " + NAME + ".ans";
signed main(void)
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(nullptr);
int tc = 0;
while (1)
{
system(GEN.c_str());
system(STD.c_str());
LL c = -clock();
system(SOL.c_str());
c += clock();
if (system(DIFF.c_str()))
{
cerr << "Wrong!\n";
return 1;
}
cerr << "OK on test case #" << ++tc << ". Time: " << (double)c / CLOCKS_PER_SEC << "s.\n";
}
return 0;
}
数据结构
并查集
struct Dsu
{
int ff[N], sz[N];
void init(int nn)
{
iota(ff + 1, ff + nn + 1, 1);
fill(sz + 1, sz + nn + 1, 1);
}
int getanc(int u) { return u == ff[u] ? u : (ff[u] = getanc(ff[u])); }
bool merge(int u, int v)
{
u = getanc(u), v = getanc(v);
if (u == v) return 0;
if (sz[u] > sz[v]) swap(u, v);
ff[u] = v;
sz[v] += sz[u];
return 1;
}
} dsu;
多项式
拉格朗日插值
LL inter(const vector<pair<LL, LL>> &pts, LL x)
{
LL ans = 0;
F(i, 0, SZ(pts))
{
LL t = 1;
F(j, 0, SZ(pts)) if (j != i)
t = t * (pts[i].first - pts[j].first) % MOD;
t = kpow(t + MOD) * pts[i].second % MOD;
F(j, 0, SZ(pts)) if (j != i)
t = t * (x - pts[j].first) % MOD;
ans += t;
}
return (ans % MOD + MOD) % MOD;
}
NTT
namespace NTT
{
constexpr int NN = 1 << 21, P = 998244353, G = 114514;
LL kpow(LL x, LL k = P - 2)
{
x = x % P;
LL r = 1;
while (k)
{
if (k & 1) r = r * x % P;
x = x * x % P;
k >>= 1;
}
return r;
}
int nn, kk, rv[NN];
void init(int n)
{
nn = 1, kk = 0;
while (nn <= n) nn <<= 1, ++kk;
rv[0] = 0;
F(i, 1, nn - 1) rv[i] = rv[i >> 1] >> 1 | (i & 1) << kk - 1;
}
void ntt(int *a, int n, int flg)
{
if (nn <= n || (nn >> 1) > n) init(n);
F(i, 0, nn - 1) if (rv[i] > i) swap(a[i], a[rv[i]]);
F(k, 0, kk - 1)
{
int l = 1 << k;
LL g = kpow(G, (P - 1) >> k + 1);
for (int i = 0; i < nn; i += l << 1)
{
LL cg = 1;
F(j, 0, l - 1)
{
LL x = a[i + j], y = a[i + j + l];
a[i + j] = (x + cg * y) % P;
a[i + j + l] = (x - cg * y) % P;
cg = cg * g % P;
}
}
}
if (flg == -1)
{
reverse(a + 1, a + nn);
LL ivn = kpow(nn);
F(i, 0, nn - 1) a[i] = a[i] * ivn % P;
}
}
}
字符串
SA
namespace SA
{
int rk[N << 1], tmp[N], tot;
int pos[N];
void calc_sa(int *sa, string s)
{
int n = s.size() - 1;
F(i, 1, 127) pos[i] = 0;
F(i, 1, n) pos[s[i]] += 1;
F(i, 1, 127) pos[i] += pos[i - 1];
FF(i, n, 1) sa[pos[s[i]]--] = i;
F(i, 1, n)
{
if (i != 1 && s[sa[i]] == s[sa[i - 1]])
rk[sa[i]] = rk[sa[i - 1]];
else rk[sa[i]] = i;
}
for (int l = 1; l <= n; l <<= 1)
{
tot = 0;
F(i, 1, n) if (sa[i] + l > n) tmp[++tot] = sa[i];
F(i, 1, n) if (sa[i] > l) tmp[++tot] = sa[i] - l;
F(i, 1, n) pos[i] = 0;
F(i, 1, n) pos[rk[tmp[i]]] += 1;
F(i, 1, n) pos[i] += pos[i - 1];
FF(i, n, 1) sa[pos[rk[tmp[i]]]--] = tmp[i];
bool flg = 0;
F(i, 1, n)
{
if (i != 1 && rk[sa[i]] == rk[sa[i - 1]]
&& rk[sa[i] + l] == rk[sa[i - 1] + l])
{
tmp[sa[i]] = tmp[sa[i - 1]];
flg = 1;
}
else tmp[sa[i]] = i;
}
F(i, 1, n) rk[i] = tmp[i];
if (!flg) break;
}
}
}
线代
矩阵
namespace Matrix
{
constexpr int M = 3;
using T = int;
struct Mat
{
T a[M][M];
};
struct Vec
{
T a[M];
};
Mat operator *(const Mat &a, const Mat &b)
{
Mat t;
memset(t.a, 0, sizeof (t.a));
F(i, 0, M - 1) F(k, 0, M - 1)
{
if (a.a[i][k] == 0)
{
continue;
}
F(j, 0, M - 1)
{
t.a[i][j] += a.a[i][k] * b.a[k][j];
}
}
return t;
}
Vec operator *(const Mat &a, const Vec &b)
{
Vec t;
memset(t.a, 0, sizeof (t.a));
F(i, 0, M - 1) F(k, 0, M - 1)
{
t.a[i] += a.a[i][k] * b.a[k];
}
return t;
}
Mat mpow(const Mat &a, LL k)
{
Mat t, aa;
aa = a;
F(i, 0, M - 1) F(j, 0, M - 1)
{
t.a[i][j] = i == j ? 1 : 0;
}
while (k)
{
if (k & 1)
{
t = t * aa;
}
aa = aa * aa;
k >>= 1;
}
return t;
}
}
数论
组合数
LL kpow(LL x, LL k = MOD - 2)
{
x = x % MOD;
LL r = 1;
while (k)
{
if (k & 1) r = r * x % MOD;
x = x * x % MOD;
k >>= 1;
}
return r;
}
int fac[N], ifac[N];
LL C(int n, int r)
{
if (r < 0 || r > n) return 0;
return fac[n] * (LL)ifac[n - r] % MOD * ifac[r] % MOD;
}
void init_comb(int nn)
{
fac[0] = 1;
F(i, 1, nn) fac[i] = fac[i - 1] * (LL)i % MOD;
ifac[nn] = kpow(fac[nn]);
FF(i, nn, 1) ifac[i - 1] = ifac[i] * (LL)i % MOD;
}
快速取模(Barrette Reduction)
struct Barrate
{
__int128 m, t;
void init(int _m)
{
m = _m;
t = ((__int128)1 << 64) / m;
}
LL operator ()(LL x)
const {
return x - m * (x * t >> 64);
}
} pmod;
LL kpow(LL x, LL k)
{
x = pmod(x);
LL r = 1;
while (k)
{
if (k & 1) r = pmod(r * x);
x = pmod(x * x);
k >>= 1;
}
return r;
}
扩展欧几里得
LL gcd(LL a, LL b)
{
LL t;
while (b) t = b, b = a % b, a = t;
return a;
}
void exgcd(LL a, LL b, LL &x, LL &y)
{
if (b) exgcd(b, a % b, y, x), y -= a / b * x;
else x = 1, y = 0;
}
LL calc_inv(LL a)
{
LL x, y;
exgcd(a, MOD, x, y);
return (x % MOD + MOD) % MOD;
}
还原分数
LL kpow(LL x, LL k, LL p)
{
LL r = 1;
while (k)
{
if (k & 1) r = (__int128)r * x % p;
x = (__int128)x * x % p;
k >>= 1;
}
return r;
}
pair<LL, LL> recover(LL x, LL p)
{
vector<LL> a;
LL invx = kpow(x, p - 2, p), pp = p;
while (x)
{
a.push_back(x);
LL t = x;
x = p % x;
p = t;
}
pair<LL, LL> res{pp, pp};
for (auto ca : a)
{
LL cb = (__int128)ca * invx % pp;
ca = min(ca, pp - ca);
cb = min(cb, pp - cb);
if (max(res.first, res.second) > max(ca, cb))
res = {ca, cb};
}
return res;
}
Miller Rabin 与 Pollard's Rho
constexpr int BS[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
mt19937_64 ran(chrono::system_clock::now().time_since_epoch().count());
LL kpow(LL x, LL k, LL mod)
{
LL r = 1;
while (k)
{
if (k & 1) r = (__int128)r * x % mod;
x = (__int128)x * x % mod;
k >>= 1;
}
return r;
}
namespace MillerRabin
{
bool check(LL n)
{
if (n == 1) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
for (auto bs : BS)
{
if (n == bs) return true;
if (n % bs == 0) return false;
if (kpow(bs, n - 1, n) != 1) return false;
LL x = n - 1;
while (!(x & 1))
{
x >>= 1;
LL t = kpow(bs, x, n);
if (t != 1)
{
if (t != n - 1) return false;
break;
}
}
}
return true;
}
}
namespace PollardRho
{
LL get_factor(LL n)
{
if (n == 4) return 2;
uniform_int_distribution<LL> gen(1, n - 1);
while (1)
{
const LL c = gen(ran);
LL f = 0, ff = 0;
do {
LL pd = 1;
F(_, 1, 128)
{
f = ((__int128)f * f + c) % n;
ff = ((__int128)ff * ff + c) % n;
ff = ((__int128)ff * ff + c) % n;
if ((__int128)pd * abs(f - ff) % n == 0) break;
pd = (__int128)pd * abs(f - ff) % n;
}
LL g = __gcd(pd, n);
if (g != 1) return g;
} while (f != ff);
}
}
vector<LL> solve(LL n)
{
if (MillerRabin::check(n)) return {n};
LL x = get_factor(n);
auto res = solve(x);
auto r = solve(n / x);
res.insert(res.end(), r.begin(), r.end());
return res;
}
}
杜教筛
namespace Phi
{
constexpr int M = (1 << 21) + 5;
LL sg(LL n)
{
return n;
}
LL sh(LL n)
{
return n * (n + 1) / 2;
}
int nn;
LL sf[M];
bool ispri[M];
vector<int> pri;
void init(int _n)
{
nn = _n;
ispri[1] = false;
F(i, 2, nn) ispri[i] = true;
sf[1] = 1;
F(i, 2, nn)
{
if (ispri[i])
{
sf[i] = i - 1;
pri.push_back(i);
}
for (auto p : pri)
{
if (i * p > nn) break;
ispri[i * p] = false;
if (i % p == 0)
{
sf[i * p] = sf[i] * p;
break;
}
else sf[i * p] = sf[i] * (p - 1);
}
sf[i] += sf[i - 1];
}
}
unordered_map<LL, LL> mem;
LL calc_sf(LL n)
{
if (n <= nn) return sf[n];
if (mem.find(n) != mem.end()) return mem[n];
LL ans = sh(n);
for (LL l = 2, r; l <= n; l = r + 1)
{
r = n / (n / l);
ans -= (sg(r) - sg(l - 1)) * calc_sf(n / l);
}
return mem[n] = ans;
}
}
网络流
Dinic(有源汇网络最大流)
namespace Dinic
{
constexpr int V = N * 2, E = N * N + N * 2;
using T = int;
int nn;
struct Edge { int v; T cap, fw; } e[E << 1];
int nxt_e[E << 1], hd_e[V], tot_e;
void init(int n)
{
nn = n;
memset(hd_e + 1, -1, sizeof (int) * nn);
tot_e = 0;
}
int ins_e(int u, int v, T c)
{
e[tot_e] = {v, c, 0};
nxt_e[tot_e] = hd_e[u], hd_e[u] = tot_e++;
e[tot_e] = {u, 0, 0};
nxt_e[tot_e] = hd_e[v], hd_e[v] = tot_e++;
return tot_e - 2;
}
int lv[V];
bool bfs(int s, int t)
{
memset(lv + 1, -1, sizeof (int) * nn);
vector<int> qu; int fr = 0;
qu.push_back(t); lv[t] = 0;
while (fr < qu.size())
{
int u = qu[fr++];
for (int i = hd_e[u]; ~i; i = nxt_e[i])
{
if (lv[e[i].v] != -1 || e[i ^ 1].cap == e[i ^ 1].fw) continue;
qu.push_back(e[i].v); lv[e[i].v] = lv[u] + 1;
}
}
return lv[s] != -1;
}
int cur_hd[V];
T flow(int u, int t, T cf)
{
if (u == t) return cf;
T sum = 0;
for (; ~cur_hd[u]; cur_hd[u] = nxt_e[cur_hd[u]])
{
int i = cur_hd[u];
if (lv[e[i].v] != lv[u] - 1) continue;
T r = flow(e[i].v, t, min(cf - sum, e[i].cap - e[i].fw));
sum += r;
e[i].fw += r, e[i ^ 1].fw -= r;
if (sum == cf) break;
}
return sum;
}
T dinic(int s, int t)
{
T sum = 0;
while (bfs(s, t))
{
memcpy(cur_hd + 1, hd_e + 1, sizeof (int) * (nn));
sum += flow(s, t, numeric_limits<T>::max());
}
return sum;
}
}
费用流
这里都是 int
类型。
单路增广
namespace CostFlow
{
constexpr int V = 5005, E = 50005;
struct Edge
{
int v, cap, fl, w;
} e[E << 1];
int nxt[E << 1], hd[V], tote;
void clr(void) { tote = 0, memset(hd, -1, sizeof (hd)); }
int ins_e(int u, int v, int c, int w)
{
e[tote] = {v, c, 0, w};
nxt[tote] = hd[u], hd[u] = tote++;
e[tote] = {u, 0, 0, -w};
nxt[tote] = hd[v], hd[v] = tote++;
return tote - 2;
}
int from[V], dis[V]; bool inque[V];
bool spfa(int s, int t)
{
queue<int> que; que.push(t);
memset(dis, 0x3f, sizeof (dis)); dis[t] = 0;
memset(inque, 0, sizeof (inque)); inque[t] = 1;
from[s] = -1;
while (!que.empty())
{
int u = que.front(); que.pop(); inque[u] = 0;
for (int i = hd[u]; i != -1; i = nxt[i])
{
if (dis[e[i].v] <= dis[u] + e[i ^ 1].w
|| e[i ^ 1].fl == e[i ^ 1].cap) continue;
dis[e[i].v] = dis[u] + e[i ^ 1].w;
from[e[i].v] = i ^ 1;
if (!inque[e[i].v]) inque[e[i].v] = 1, que.push(e[i].v);
}
}
return from[s] != -1;
}
pair<int, int> flow(int s, int t)
{
int sf = 0, sw = 0;
while (spfa(s, t))
{
int cf = INT_MAX, cw = 0;
for (int u = s; u != t; u = e[from[u]].v)
{
int id = from[u];
MIN(cf, e[id].cap - e[id].fl);
cw += e[id].w;
}
sf += cf, sw += cf * cw;
for (int u = s; u != t; u = e[from[u]].v)
{
int id = from[u];
e[id].fl += cf, e[id ^ 1].fl -= cf;
}
}
return {sf, sw};
}
}
多路增广
namespace CostFlow
{
constexpr int V = 5005, E = 50005;
struct Edge
{
int v, cap, fl, w;
} e[E << 1];
int nxt[E << 1], hd[V], cur[V], tote;
void clr(void) { tote = 0, memset(hd, -1, sizeof (hd)); }
int ins_e(int u, int v, int c, int w)
{
e[tote] = {v, c, 0, w};
nxt[tote] = hd[u], hd[u] = tote++;
e[tote] = {u, 0, 0, -w};
nxt[tote] = hd[v], hd[v] = tote++;
return tote - 2;
}
int from[V], dis[V]; bool inque[V];
bool spfa(int s, int t)
{
queue<int> que; que.push(t);
memset(dis, 0x3f, sizeof (dis)); dis[t] = 0;
memset(inque, 0, sizeof (inque)); inque[t] = 1;
from[s] = -1;
while (!que.empty())
{
int u = que.front(); que.pop(); inque[u] = 0;
for (int i = hd[u]; i != -1; i = nxt[i])
{
if (dis[e[i].v] <= dis[u] + e[i ^ 1].w
|| e[i ^ 1].fl == e[i ^ 1].cap) continue;
dis[e[i].v] = dis[u] + e[i ^ 1].w;
from[e[i].v] = i ^ 1;
if (!inque[e[i].v]) inque[e[i].v] = 1, que.push(e[i].v);
}
}
return from[s] != -1;
}
vector<int> rst;
bool vis[V];
pair<int, int> go_flow(int u, int t, int sf)
{
if (u == t) return {sf, 0};
rst.push_back(u);
vis[u] = 1;
int cf = 0, cw = 0;
for (int &i = cur[u]; i != -1; i = nxt[i])
{
if (e[i].cap == e[i].fl || dis[e[i].v] + e[i].w != dis[u]
|| vis[e[i].v]) continue;
auto ret = go_flow(e[i].v, t, min(e[i].cap - e[i].fl, sf - cf));
int tf = ret.first;
cf += tf;
cw += ret.second + tf * e[i].w;
e[i].fl += tf, e[i ^ 1].fl -= tf;
if (cf == sf) break;
}
vis[u] = 0;
return {cf, cw};
}
pair<int, int> flow(int s, int t)
{
int sf = 0, sw = 0;
memcpy(cur, hd, sizeof (cur));
while (spfa(s, t))
{
auto ret = go_flow(s, t, INT_MAX);
sf += ret.first, sw += ret.second;
for (int u : rst) cur[u] = hd[u];
rst.clear();
}
return {sf, sw};
}
}
Capcity Scaling
真正的(弱)多项式复杂度 \(\mathrm O(nm^2\log U)\) 的费用流,然而会跑很满,在多数 CNOI 费用流题目中无法通过。
最小费用最大流需要一些手动操作,封装不太好。
// Author: kyEEcccccc
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
#define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max(a, b))
#define MIN(a, b) ((a) = min(a, b))
#define SZ(a) ((int)((a).size()) - 1)
namespace CostFlow
{
constexpr int V = 505, E = 505;
struct Edge
{
int u, v; LL cap, ccap, fl, w;
} e[E << 1];
int nxt[E << 1], hd[V], cur[V], tote;
void clr(void) { tote = 0, memset(hd, -1, sizeof (hd)); }
int ins_e(int u, int v, LL c, LL w)
{
e[tote] = {u, v, c, 0, 0, w};
nxt[tote] = hd[u], hd[u] = tote++;
e[tote] = {v, u, 0, 0, 0, -w};
nxt[tote] = hd[v], hd[v] = tote++;
return tote - 2;
}
int from[V]; LL dis[V]; bool inque[V];
bool spfa(int s, int t)
{
queue<int> que; que.push(t);
memset(dis, 0x3f, sizeof (dis)); dis[t] = 0;
memset(inque, 0, sizeof (inque)); inque[t] = 1;
from[s] = -1;
while (!que.empty())
{
int u = que.front(); que.pop(); inque[u] = 0;
for (int i = hd[u]; i != -1; i = nxt[i])
{
if (dis[e[i].v] <= dis[u] + e[i ^ 1].w
|| e[i ^ 1].fl == e[i ^ 1].ccap) continue;
dis[e[i].v] = dis[u] + e[i ^ 1].w;
from[e[i].v] = i ^ 1;
if (!inque[e[i].v]) inque[e[i].v] = 1, que.push(e[i].v);
}
}
return from[s] != -1;
}
pair<LL, LL> flow(void)
{
LL sf = 0, sw = 0;
int mxk = 0;
for (int i = 0; i < tote; i += 2) while (e[i].cap >> mxk) ++mxk;
FF(k, mxk - 1, 0)
{
sf <<= 1, sw <<= 1;
F(i, 0, tote - 1) e[i].ccap <<= 1, e[i].fl <<= 1;
for (int i = 0; i < tote; i += 2) if (e[i].cap >> k & 1)
{
if (!spfa(e[i].v, e[i].u) || dis[e[i].v] + e[i].w >= 0)
{
++e[i].ccap;
continue;
}
++e[i].ccap;
++e[i].fl, --e[i ^ 1].fl;
for (int u = e[i].v; u != e[i].u; u = e[from[u]].v)
++e[from[u]].fl, --e[from[u] ^ 1].fl;
++sf, sw += dis[e[i].v] + e[i].w;
}
}
return {sf, sw};
}
}
signed main(void)
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(nullptr);
int n, m, s, t; cin >> n >> m >> s >> t;
CostFlow::clr();
LL ex = 0, sum_c = 0, sum_w = 0;
F(i, 1, m)
{
int u, v; LL c, w; cin >> u >> v >> c >> w;
if (u == v)
{
if (w < 0) ex += w * c;
continue;
}
sum_c += c;
sum_w += abs(w);
CostFlow::ins_e(u, v, c, w);
}
int ts = CostFlow::ins_e(t, s, sum_c, -sum_w - 1);
LL res = CostFlow::flow().second;
res -= CostFlow::e[ts].fl * CostFlow::e[ts].w;
cout << CostFlow::e[ts].fl << ' ' << res + ex << endl;
return 0;
}
计算几何
向量
struct Vec
{
double x, y;
double len(void) const { return sqrt(x * x + y * y); }
double angle(void) const { return atan2(y, x); }
Vec operator +(const Vec& o) const { return {x + o.x, y + o.y}; }
Vec& operator +=(const Vec& o)
{
x += o.x, y += o.y;
return *this;
}
Vec operator -(const Vec& o) const { return {x - o.x, y - o.y}; }
Vec& operator -=(const Vec& o)
{
x -= o.x, y -= o.y;
return *this;
}
friend Vec operator *(const Vec& v, double p) { return {v.x * p, v.y * p}; }
friend Vec operator *(double p, const Vec& v) { return {v.x * p, v.y * p}; }
Vec& operator *= (double p)
{
x *= p, y *= p;
return *this;
}
friend Vec operator /(const Vec& v, double p) { return {v.x / p, v.y / p}; }
friend Vec operator /(double p, const Vec& v) { return {v.x / p, v.y / p}; }
Vec& operator /= (double p)
{
x /= p, y /= p;
return *this;
}
double dot(const Vec& o) const { return x * o.x + y * o.y; }
double times(const Vec& o) const { return x * o.y - y * o.x; }
};