T1圆圈游戏
暴力DP有60分,设包含圆i的最小的圆是fa[i],那没最终会的得到一棵树,对于一棵子树,选了根节点就不能选子树内其它点,f[i]=max(w[i],\(\sum f[son]\)).
瓶颈就在怎么建图,因为圆不相交相切,所以扫描线的时候相对位置不会发生改变,用set维护一下就好啦。
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<cstdio>
#include<set>
using namespace std;
int n, ans, tmp, lx;
const int N = 200010;
int f[N], fa[N];
struct yuan
{
int x, y, r, w;
double h(int dir) {return y + dir * sqrt(1.0 * r * r - (1.0 * lx - x) * (lx - x));}
} y[N];
struct hu
{
int dir, id;
friend bool operator <(const hu &a, const hu &b)
{return a.id == b.id ? a.dir < b.dir : (y[a.id].h(a.dir) < y[b.id].h(b.dir));}
} b[N];
inline int read()
{
int res = 0; char ch = getchar(); bool XX = false;
for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
return XX ? -res : res;
}
int my(hu a, hu b) {return y[a.id].x - a.dir * y[a.id].r < y[b.id].x - a.dir * y[b.id].r;}
set<hu>s;
set<hu>::iterator it;
signed main()
{
cin >> n;
for (int i = 1; i <= n; ++i)
{
y[i].x = read(); y[i].y = read(); y[i].r = read(); y[i].w = read();
b[++tmp] = (hu) {1, i}, b[++tmp] = (hu) { -1, i};
}
sort(b + 1, b + 1 + tmp, my);
for (int i = 1; i <= tmp; ++i)
{
int id = b[i].id, dir = b[i].dir;
lx = y[id].x - dir * y[id].r;
if (dir == 1)
{
if (!s.empty())
{
it = s.upper_bound(b[i]);
if (it != s.end())
fa[id] = (it->dir == 1) ? it->id : fa[it->id];
}
s.insert((hu) {1, id}); s.insert((hu) { -1, id});
} else
{
s.erase((hu) {1, id}); s.erase((hu) { -1, id});
f[fa[id]] += max(f[id], y[id].w);
}
}
cout << f[0];
return 0;
}
T2划分序列
很明显的二分答案,关键就在于check。
设f1[i]表示在i处划分一下,在满足mid的条件下最少划分次数,f2[i]表示最多的划分次数,若\(f1[n] \le k \le f2[n]\)则mid合法。
暴力dp是\(O(n^2)\)的,发现更新f1,f2的是区间最大/最小值,用树状数组维护一下就可以了。
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
int n, k, ans, tot;
const int N = 100010, inf = 1 << 30;
int a[N], f1[N], f2[N], tr1[N], tr2[N], b[N], t1[N], t2[N];
inline int read()
{
int res = 0; char ch = getchar(); bool XX = false;
for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
return XX ? -res : res;
}
inline int lowbit(int x) {return x & (-x);}
void change1(int pos, int val)
{
for (; pos; pos -= lowbit(pos))tr1[pos] = min(tr1[pos], val);
}
int ask1(int pos)
{
int res = inf;
for (; pos <= tot; pos += lowbit(pos))res = min(res, tr1[pos]);
return res;
}
void change2(int pos, int val)
{
for (; pos; pos -= lowbit(pos))tr2[pos] = max(tr2[pos], val);
}
int ask2(int pos)
{
int res = -inf;
for (; pos <= tot; pos += lowbit(pos))res = max(res, tr2[pos]);
return res;
}
int check2(int mid)
{
tot = 0;
for (int i = 0; i <= n; ++i)
{
b[++tot] = a[i];
b[++tot] = a[i] - mid;
}
sort(b + 1, b + 1 + tot);
tot = unique(b + 1, b + 1 + tot) - b - 1;
for (int i = 0; i <= n; ++i)
{
t1[i] = lower_bound(b + 1, b + 1 + tot, a[i]) - b;
t2[i] = lower_bound(b + 1, b + 1 + tot, a[i] - mid) - b;
}
for (int i = 1; i <= tot; ++i)tr1[i] = inf, tr2[i] = -inf;
change1(t1[0], 0); change2(t1[0], 0);
for (int i = 1; i <= n; ++i)
{
f1[i] = ask1(t2[i]) + 1; f2[i] = ask2(t2[i]) + 1;
change1(t1[i], f1[i]); change2(t1[i], f2[i]);
}
return f1[n] <= k && k <= f2[n];
}
void solve2()
{
int l = 0, r = 0;
for (int i = 1; i <= n; ++i)
if (a[i] < 0)l += a[i];
else r += a[i];
for (int i = 1; i <= n; ++i)a[i] += a[i - 1];
while (l <= r)
{
int mid = (l + r) / 2;
if (check2(mid))ans = mid, r = mid - 1;
else l = mid + 1;
}
cout << ans;
}
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; ++i)a[i] = read();
solve2();
return 0;
}
T3生成树求和
由于加法不进位,所以每一位我们单独考虑。
用矩阵树定理求的是\(\sum\)所有生成树边权的积,而这里我们需要求的是边权和,所以三次单位根的乘法来完成加法。
最后还要求解一下三元一次方程,可以选择高斯消元,也可以像我一样手动消元。
#include<iostream>
#include<cstring>
#include<cstdio>
#define int long long
#define LL long long
using namespace std;
int n, m, ans;
const LL N = 105, mod = 1e9 + 7, inv2 = (mod + 1) >> 1, inv3 = (mod + 1) / 3, sqrt3 = 82062379;
int fr[N * N], to[N * N], val[N * N], bin[20];
inline int read()
{
int res = 0; char ch = getchar(); bool XX = false;
for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
return XX ? -res : res;
}
LL ksm(LL a, LL b, LL mod)
{
LL res = 1;
for (; b; b >>= 1, a = a * a % mod)
if (b & 1)res = res * a % mod;
return res;
}
struct xu
{
LL a, b;
xu(int _a = 0, int _b = 0) {a = _a, b = _b;}
friend xu operator +(const xu &x, const xu &y)
{return (xu) {(x.a + y.a) % mod, (x.b + y.b) % mod};}
friend xu operator -(const xu &x, const xu &y)
{return (xu) {(x.a - y.a + mod) % mod, (x.b - y.b + mod) % mod};}
friend xu operator *(const xu &x, const xu &y)
{return (xu) {(x.a * y.a % mod - x.b * y.b % mod + mod) % mod, (x.a * y.b + x.b * y.a) % mod};}
friend xu operator *(const xu &x, const int &y)
{return (xu) {(x.a * y) % mod, (x.b * y) % mod};}
friend xu operator /(const xu &x, const int &y)
{
int inv = ksm(y, mod - 2, mod);
return (xu) {(x.a * inv) % mod, (x.b * inv) % mod};
}
friend xu operator /(const xu &x, const xu &y)
{
return x * (xu) {y.a, mod - y.b} / ((y.a * y.a % mod + y.b * y.b % mod) % mod);
}
} w[3], invw[3], a[N][N], coef[3], inv;
xu work()
{
xu res = xu(1, 0);
for (int i = 1; i < n; ++i)
{
for (int j = i; j < n; ++j)
if (a[j][i].a || a[j][i].b)
{
if (i == j)break;
swap(a[i], a[j]); res = res * (mod - 1);
break;
}
for (int j = i + 1; j < n; ++j)
{
inv = a[j][i] / a[i][i];
for (int k = i; k < n; ++k)a[j][k] = a[j][k] - inv * a[i][k];
}
res = res * a[i][i];
}
return res;
}
void solve(xu *p)
{
xu tmp[3];
tmp[0] = p[0]; tmp[1] = p[1]; tmp[2] = p[2];
p[0] = tmp[0] + tmp[1] + tmp[2];
p[1] = tmp[0] + tmp[1] * invw[1] + tmp[2] * invw[2];
p[2] = tmp[0] + tmp[1] * invw[2] + tmp[2] * invw[1];
for (int i = 0; i <= 2; ++i)p[i] = p[i] * inv3;
}
LL suan(int p)
{
int k;
xu w0, w1, w2, c;
for (int i = 0; i <= 2; ++i)
{
memset(a, 0, sizeof(a));
w0 = xu(1, 0); w1 = w[i]; w2 = w[(i + i) % 3];
for (int j = 1; j <= m; ++j)
{
k = val[j] / bin[p] % 3;
c = (k == 1 ? w1 : (k == 2 ? w2 : w0));
a[fr[j]][fr[j]] = a[fr[j]][fr[j]] + c;
a[to[j]][to[j]] = a[to[j]][to[j]] + c;
a[fr[j]][to[j]] = a[fr[j]][to[j]] - c;
a[to[j]][fr[j]] = a[to[j]][fr[j]] - c;
}
coef[i] = work();
}
solve(coef);
return ((coef[1].a + coef[2].a + coef[2].a) % mod) * bin[p] % mod;
}
signed main()
{
freopen("sum.in", "r", stdin);
freopen("sum.out", "w", stdout);
cin >> n >> m;
w[0] = xu(1, 0); w[1] = xu(mod - inv2, sqrt3 * inv2 % mod);
w[2] = xu(mod - inv2, mod - sqrt3 * inv2 % mod);
invw[0] = xu(1, 0) / w[0]; invw[1] = xu(1, 0) / w[1]; invw[2] = xu(1, 0) / w[2];
for (int i = 1; i <= m; ++i)fr[i] = read(), to[i] = read(), val[i] = read();
bin[0] = 1;
for (int i = 1; i <= 10; ++i)bin[i] = bin[i - 1] * 3;
for (int i = 0; i <= 10; ++i)(ans += suan(i)) %= mod;
cout << ans;
return 0;
}