给n个点,每条边的权值有一定概率出现,题目给出。权值为0表示不存在。问对于若干个S求最小生成树恰好为S的概率。
#include
using namespace std;
const int MAXN = 50;
const int MAXK = 6;
const int mod = 1e9 + 7;
const int inv100 = 570000004;
inline int qpow(int a, int b) {
int re = 1;
while(b) {
if(b&1) re = 1ll * re * a % mod;
a = 1ll * a * a % mod; b >>= 1;
}
return re;
}
inline int mul(int x, int y) { return 1ll * x * y % mod; }
inline int add(int x, int y) { return (x + y) % mod; }
int c[MAXN][MAXN], inv[MAXN*MAXK], p[MAXK], sp[MAXK], pwx[MAXK];
int t1[MAXN][MAXN], t2[MAXN][MAXN], f[MAXK][MAXN], g[MAXN][MAXN];
int v[MAXN*MAXK];
vector<int>interpolation(int y[], int n) { //拉格朗日插值求多项式系数
vector<int>P(n+1), now(n+1), re(n); P[0] = 1;
for(int i = 0; i < n; ++i) for(int j = i+1; ~j; --j) P[j] = add(j?P[j-1]:0, -mul(i, P[j]));
for(int i = 0; i < n; ++i) {
for(int j = n-1; ~j; --j) now[j] = add(P[j+1], mul(i, now[j+1]));
int tmp = (((n-i-1)&1)?-1:1) * mul(mul(inv[i], inv[n-i-1]), y[i]);
for(int j = 0; j < n; ++j) re[j] = add(re[j], mul(tmp, now[j]));
}
return re;
}
void solve() {
int n, k;
scanf("%d%d", &n, &k);
for(int i = 0; i <= k; ++i) scanf("%d", &p[i]), p[i] = mul(p[i], inv100);
sp[k+1] = 0;
for(int i = k; ~i; --i) sp[i] = add(sp[i+1], p[i]);
for(int x = 0; x <= (k-1)*(n-1); ++x) {
pwx[0] = 1;
for(int i = 1; i < k; ++i) pwx[i] = mul(pwx[i-1], x);
f[0][1] = 1;
for(int t = 1; t <= k; ++t) {
for(int i = 0; i <= n; ++i)
for(int j = 1; i+j <= n; ++j) {
t2[i][j] = qpow(p[0] + sp[t+1], i*j); //用>t的边
t1[i][j] = add(qpow(p[0] + sp[t], i*j), -t2[i][j]); //用>=t的边且至少一条=t
}
for(int i = 1; i <= n; ++i)
for(int j = 0; i+j <= n; ++j)
g[i][j] = (j == 0);
for(int s = 1; s <= n; ++s) {
f[t][s] = 0;
for(int i = 1; i <= s; ++i)
f[t][s] = add(f[t][s], mul(mul(f[t-1][i], g[i][s-i]), c[s-1][i-1]));
for(int i = 1; i <= n; ++i)
for(int j = n-i-s; j >= 0; --j)
for(int k = 1, tmp = 1, bas = mul(pwx[t-1], mul(t1[i][s], f[t][s])); i+j+k*s <= n; ++k) {
tmp = mul(tmp, mul(bas, mul(t2[j+(k-1)*s][s], c[j+k*s][s])));
g[i][j+k*s] = add(g[i][j+k*s], mul(mul(g[i][j], tmp), inv[k]));
}
}
}
v[x] = f[k][n];
//printf("v(%d) = %d\n", x, v[x]);
}
vector<int>ans = interpolation(v, (k-1)*(n-1)+1);
for(int i = 0; i <= (k-1)*(n-1); ++i)
printf("%d%c", add(ans[i], mod), " \n"[i==(k-1)*(n-1)]);
}
void pre() {
c[0][0] = 1;
for(int i = 1; i < MAXN; ++i) {
c[i][0] = c[i][i] = 1;
for(int j = 1; j < i; ++j)
c[i][j] = add(c[i-1][j-1], c[i-1][j]);
}
inv[0] = inv[1] = 1;
for(int i = 2; i < MAXN*MAXK; ++i)
inv[i] = mul(mod - mod/i, inv[mod%i]);
for(int i = 2; i < MAXN*MAXK; ++i)
inv[i] = mul(inv[i], inv[i-1]);
}
int main() {
pre();
int T; scanf("%d", &T);
while(T-->0)solve();
}
略
显然从大到小贪心,买进来能够让答案更优就买。如果一个买不了剩下的一定也买不了,直接break就行了。
#include
using namespace std;
const int MAXN = 10005;
int n;
double p[MAXN];
int main () {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%lf", &p[i]);
sort(p + 1, p + n + 1);
double ans = p[n], tmp = 1-p[n];
for(int i = n-1; i; --i) {
if(ans * (1-p[i]) + tmp * p[i] > ans) {
ans = ans * (1-p[i]) + tmp * p[i];
tmp *= 1-p[i];
}
else break;
}
printf("%.12f\n", ans);
}
}
链接
又双叒叕学习了一发SAM
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 200005;
LL W[MAXN];
char s[MAXN];
int pos[MAXN], Right[MAXN], dep[MAXN], f[MAXN][19], c[MAXN];
vector<int>G[MAXN];
struct Linear_Basis {
LL a[58]; ULL ans;
inline void init() { ans = 0; memset(a, 0, sizeof a); }
inline bool ins(LL x) {
LL tmp = x;
for(int i = 57; ~i; --i)
if(x&(1ll<<i)) {
if(!a[i]) { a[i] = x; ans += tmp; return 1; }
x ^= a[i];
}
return 0;
}
}LB[MAXN];
struct Suffix_Automation {
int tot, cur;
struct node{ int len, fa, ch[26]; } t[MAXN];
inline void init() {
cur = tot = 1;
memset(t, 0, sizeof t);
memset(Right, 0, sizeof Right);
}
inline void ins(int x) {
int p = cur; ++Right[cur = ++tot]; t[cur].len = t[p].len + 1;
for(; p && !t[p].ch[x]; p = t[p].fa) t[p].ch[x] = cur;
if(!p) { t[cur].fa = 1; return; }
int q = t[p].ch[x];
if(t[p].len + 1 == t[q].len) { t[cur].fa = q; return; }
int clone = ++tot;
t[clone].len = t[p].len + 1;
memcpy(t[clone].ch, t[q].ch, sizeof t[q].ch);
t[clone].fa = t[q].fa;
t[q].fa = t[cur].fa = clone;
for(; p && t[p].ch[x] == q; p = t[p].fa) t[p].ch[x] = clone;
}
void dfs(int u) {
for(int v, i = G[u].size()-1; ~i; --i) {
v = G[u][i];
dep[v] = dep[u] + 1;
f[v][0] = u;
dfs(v);
Right[u] += Right[v];
}
}
inline void build() {
memset(G, 0, sizeof G);
for(int i = 2; i <= tot; ++i) G[t[i].fa].push_back(i);
dfs(1);
for(int j = 1; j < 19; ++j)
for(int i = 1; i <= tot; ++i)
f[i][j] = f[f[i][j-1]][j-1];
}
}SAM;
inline bool cmp(int i, int j) { return W[Right[i]] > W[Right[j]]; }
int main() {
int T, n, q, x, y; scanf("%d", &T);
while(T-->0) {
scanf("%d%s", &n, s+1);
SAM.init();
for(int i = 1; i <= n; ++i)
SAM.ins(s[i]-'a'), pos[i] = SAM.cur;
for(int i = 1; i <= n; ++i) scanf("%lld", &W[i]);
SAM.build();
int tot = SAM.tot;
for(int i = 1; i <= tot; ++i)
LB[i].init(), c[i] = i;
sort(c + 1, c + tot + 1, cmp);
for(int i = 1; i <= tot; ++i)
for(int p = c[i]; p; p = f[p][0])
if(!LB[p].ins(W[Right[c[i]]])) break;
scanf("%d", &q);
while(q-->0) {
scanf("%d%d", &x, &y);
int len = y-x+1, p = pos[y];
for(int i = 18; ~i; --i)
if(SAM.t[f[p][i]].len >= len) p = f[p][i];
printf("%llu\n", LB[p].ans);
}
}
}
略
枚举第一个集合的x最大值k,那么另一个集合y的最大值一定要大于等于xi>k的所有yi
所以倒序枚举k,线段树维护就行了。
#include
using namespace std;
typedef long long LL;
#define x first
#define y second
const int MAXN = 100005;
int n, nx, ny;
pair<LL,LL>p[MAXN];
LL vx[MAXN], vy[MAXN];
int Mn[MAXN<<2], Mx[MAXN<<2], cnt[MAXN];
void build(int i, int l, int r) {
Mn[i] = ny+1;
Mx[i] = 0;
if(l == r) { cnt[l] = 0; return; }
int mid = (l + r) >> 1;
build(i<<1, l, mid);
build(i<<1|1, mid+1, r);
}
void mdf(int i, int l, int r, int p, int v) {
if(l == r) {
cnt[p] += v;
if(!cnt[p]) Mn[i] = ny+1, Mx[i] = 0;
else Mn[i] = Mx[i] = l;
return;
}
int mid = (l + r) >> 1;
if(p <= mid) mdf(i<<1, l, mid, p, v);
else mdf(i<<1|1, mid+1, r, p, v);
Mn[i] = min(Mn[i<<1], Mn[i<<1|1]);
Mx[i] = max(Mx[i<<1], Mx[i<<1|1]);
}
int Min(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) return Mn[i];
int mid = (l + r) >> 1, re = ny+1;
if(L <= mid) re = min(re, Min(i<<1, l, mid, L, R));
if(R > mid) re = min(re, Min(i<<1|1, mid+1, r, L, R));
return re;
}
int Max(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) return Mx[i];
int mid = (l + r) >> 1, re = 0;
if(L <= mid) re = max(re, Max(i<<1, l, mid, L, R));
if(R > mid) re = max(re, Max(i<<1|1, mid+1, r, L, R));
return re;
}
int main () {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
nx = ny = 0;
for(int i = 1; i <= n; ++i) {
scanf("%lld%lld", &p[i].x, &p[i].y);
vx[++nx] = p[i].x;
vy[++ny] = p[i].y;
}
sort(p + 1, p + n + 1);
sort(vx + 1, vx + nx + 1);
nx = unique(vx + 1, vx + nx + 1) - vx - 1;
sort(vy + 1, vy + ny + 1);
ny = unique(vy + 1, vy + ny + 1) - vy - 1;
for(int i = 1; i <= n; ++i) {
p[i].x = lower_bound(vx + 1, vx + nx + 1, p[i].x) - vx;
p[i].y = lower_bound(vy + 1, vy + ny + 1, p[i].y) - vy;
}
int cur = n, mx = 1; LL ans = 1ll<<62;
build(1, 1, ny);
for(int i = 1; i <= n; ++i) mdf(1, 1, ny, p[i].y, 1);
for(int i = n; i >= 1; --i) {
while(cur && p[cur].x > p[i].x) mx = max(mx, (int)p[cur].y), --cur;
mdf(1, 1, ny, p[i].y, -1);
int j = lower_bound(vy + mx, vy + ny + 1, vx[p[i].x]) - vy, tmp;
if(j <= ny && (tmp=Min(1, 1, ny, j, ny)) <= ny) ans = min(ans, vy[tmp] - vx[p[i].x]);
--j; if(j >= mx && (tmp=Max(1, 1, ny, mx, j)) >= mx) ans = min(ans, vx[p[i].x] - vy[tmp]);
mdf(1, 1, ny, p[i].y, 1);
}
printf("%lld\n", ans);
}
}
一棵n个点的树,每个点有权值ai,一个连通诱导子图的密度定义为所有点权的平均值,你可以关闭一些点,然后树的美丽度定义为剩下的点的所有连通诱导子图(大于等于两个点)的密度的最大值。求使得树的美丽度<=X的关闭点的方案数。点的度数<=5
诱导子图的定义为点集V中的点之间的边都在边集E中的子图。在树上的连通诱导子图就是树上的一个连通块。
一句话题意:求删点的方案数,使得剩下的点数大于1的连通块的权值平均值的最大值小于等于X。
首先观察到,从所有直径 ≤ 2 的子图中一定可以找到 density 最大的子图,否则沿着直径中间的边断开可以得到两棵至少有两个点的树,其中至少有一棵树的 density 不会更小。于是只需要考虑所有直径 ≤ 2 的子图,只有 O ( n 2 d e g ) O(n2^{deg}) O(n2deg) 个,每一个满足 density > x 子图中所有点不能同时亮。对每个点记录父亲、自己、以及每个儿子的状态,预处理出所有不合法状态之后在树上 dp 即可。
#include
using namespace std;
const int MAXN = 35005;
const int mod = 1e9 + 7;
int n, X, a[MAXN], f[MAXN][3], tmp[10];
// self fa
//0 N ?
//1 Y N
//2 Y Y
vector<int>G[MAXN];
inline void mul(int &x, int y) { x = 1ll * x * y % mod; }
inline void add(int &x, int y) { x = (x + y) % mod; }
inline bool cmp(int x, int y) { return x > y; }
void dfs(int u, int ff) {
f[u][0] = 1; f[u][1] = f[u][2] = 0;
int siz = G[u].size();
for(int i = 0; i < siz; ++i)if(G[u][i] == ff){ swap(G[u][i], G[u][siz-1]); G[u].pop_back(); break; }
siz = G[u].size();
for(int v : G[u]) dfs(v, u), mul(f[u][0], (f[v][0] + f[v][1]) % mod);
for(int s = 0; s < 1<<siz; ++s) {
int g = 1, cur = 0;
for(int i = 0; i < siz; ++i)
if(s&(1<<i)) tmp[++cur] = a[G[u][i]], mul(g, f[G[u][i]][2]);
else mul(g, f[G[u][i]][0]);
if(!g) continue;
sort(tmp + 1, tmp + cur + 1, cmp);
int sum1 = a[u], sum2 = a[u] + a[ff];
bool flg1 = 1, flg2 = (sum2 <= X*2);
for(int i = 1; i <= cur && flg1; ++i)
flg1 &= ((sum1 += tmp[i]) <= (X*(i+1))),
flg2 &= ((sum2 += tmp[i]) <= (X*(i+2)));
if(flg1) {
add(f[u][1], g);
if(flg2) add(f[u][2], g);
}
}
}
int main() {
int T; scanf("%d", &T);
while(T --> 0) {
scanf("%d%d", &n, &X);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]), G[i].clear();
for(int i = 1, x, y; i < n; ++i) {
scanf("%d%d", &x, &y);
G[x].push_back(y),
G[y].push_back(x);
}
dfs(1, 0);
printf("%d\n", (f[1][0] + f[1][1]) % mod);
}
}
略
二分答案+判断"线段"是否有交
然后写正解死活不过。
然后暴力+优化过了
650ms 目前hdu rk2
.
暴力艹正解
不过这个点到线段距离是自己写的,有点丑。更短的方法是点积判断钝角锐角,然后分类用叉积或者两点间距离算答案。
#include
using namespace std;
typedef double db;
const double eps = 1e-8;
const int MAXN = 10005;
inline db sqr(db x) { return x*x; }
struct point {
db x, y; point(){}
point(db x, db y):x(x), y(y){}
inline point operator -(const point &o)const { return point(x-o.x, y-o.y); }
inline point operator +(const point &o)const { return point(x+o.x, y+o.y); }
inline point operator *(const db &o)const { return point(x*o, y*o); }
inline db mo() { return sqrt(sqr(x) + sqr(y)); }
};
struct line {
point u, v; line(){}
line(point u, point v):u(u), v(v){}
inline bool operator <(const line &o)const {
return u.x < o.u.x;
}
}L[MAXN];
inline db dis1(point A, point B) { return sqrt(sqr(A.x-B.x) + sqr(A.y-B.y)); }
inline db cross(point A, point B) { return A.x * B.y - A.y * B.x; }
inline db det(db A, db B, db C, db D) { return A * D - B * C; }
inline int sgn(double x) { return x > eps ? 1 : x < -eps ? -1 : 0; }
inline int sgndir(line A, point B) { return sgn(cross(A.v-A.u, B-A.u)); }
inline bool jiao(line A, line B) {
return sgndir(A, B.u) * sgndir(A, B.v) <= 0
&& sgndir(B, A.u) * sgndir(B, A.v) <= 0;
}
inline db dis2(point A, line B) {
point v2 = B.v - B.u, v1 = v2;
swap(v1.x, v1.y); v1.y=-v1.y; // rotate pi/2
// A + k1*v1 = B.u + k2*v2
double D = det(-v1.x, v2.x, -v1.y, v2.y);
assert(D);
double k1 = det(A.x-B.u.x, v2.x, A.y-B.u.y, v2.y) / D;
double k2 = det(-v1.x, A.x-B.u.x, -v1.y, A.y-B.u.y) / D;
if(k2 >= 0 && k2 <= 1) return fabs(k1) * v1.mo();
if(k2 < 0) return dis1(A, B.u);
return dis1(A, B.v);
}
inline db dis3(line A, line B) {
if(jiao(A, B)) return 0;
return min(min(dis2(A.u, B), dis2(A.v, B)), min(dis2(B.u, A), dis2(B.v, A)));
}
int n;
int main() {
int T; scanf("%d", &T);
while(T-->0) {
scanf("%d", &n);
for(int i = 1, A, B, C, D; i <= n; ++i) {
scanf("%d%d%d%d", &A, &B, &C, &D);
L[i] = line(point(A, B), point(C, D));
if(L[i].u.x > L[i].v.x) swap(L[i].u, L[i].v);
}
sort(L + 1, L + n + 1);
db ans = dis3(L[1], L[2]);
for(int i = 1; i <= n; ++i)
for(int j = i+1; j <= n; ++j) {
if(L[j].u.x - L[i].v.x > ans) break; //优化..
ans = min(ans, dis3(L[i], L[j]));
}
printf("%.12f\n", ans);
}
}
略
放上官方题解:
#include
using namespace std;
#define x first
#define y second
#define pii pair
const int MAXN = 100005;
const int MAXM = 200005;
const int INF = 1<<30;
pii p[MAXN];
int g[MAXM], h[MAXM], f[MAXM];
int n, N, arr[MAXM], cur, tot;
inline bool cmpg(int a, int b) { return a > b; }
inline bool cmph(pii a, pii b) { return a.x+a.y > b.x+b.y; }
void Calc_g() {
memset(g, -0x7f, sizeof g); g[0] = 0;
sort(arr + 1, arr + cur + 1, cmpg);
for(int i = 1; i <= cur; ++i) g[i] = g[i-1] + arr[i];
}
int Mxa[MAXN], Mnb[MAXN];
void Calc_h() {
memset(h, -0x7f, sizeof h); h[0] = 0;
sort(p + 1, p + tot + 1, cmph);
Mxa[tot+1] = -INF;
for(int i = tot; i >= 1; --i) Mxa[i] = max(Mxa[i+1], p[i].x);
Mnb[0] = INF;
for(int i = 1; i <= tot; ++i) Mnb[i] = min(Mnb[i-1], p[i].y);
for(int i = 1; i <= tot; ++i)
h[i<<1] = h[(i-1)<<1] + p[i].x + p[i].y;
for(int i = 0; i <= tot; ++i) {
h[2*i+1] = max(h[2*i+1], h[i<<1] + Mxa[i+1]);
if(i)
h[2*i-1] = max(h[2*i-1], h[i<<1] - Mnb[i]);
}
}
int q[MAXM], k[MAXM];
int calc(int i, int j) { return h[j] + g[i-j]; }
int getk(int ii, int jj) {
int l = jj, r = N, mid;
while(l < r) {
mid = (l + r) >> 1;
if(calc(mid, ii) <= calc(mid, jj)) r = mid;
else l = mid+1;
}
if(calc(l, ii) <= calc(l, jj)) return l;
return r+1;
}
void Calc_f() {
f[0] = 0;
memset(k, 0, sizeof k);
int s = 1, t = 0;
q[++t] = 0;
for(int i = 1; i <= N; ++i) {
while(s < t && calc(k[t-1], q[t]) <= calc(k[t-1], i)) --t;
k[t] = getk(q[t], i); q[++t] = i;
while(s < t && k[s] <= i) ++s;
f[i] = calc(i, q[s]);
}
}
int main() {
int T; scanf("%d", &T);
while(T-->0) {
scanf("%d", &n); N = n<<1;
tot = cur = 0;
for(int i = 1, a, b; i <= n; ++i) {
scanf("%d%d", &a, &b);
if(a > b) arr[++cur] = a, arr[++cur] = b;
else p[++tot] = make_pair(a, b);
}
Calc_g(); Calc_h(); Calc_f();
for(int i = 1; i <= N; ++i)
printf("%d%c", f[i], " \n"[i==N]);
}
}
略
模拟
#include
using namespace std;
const int MAXN = 2005;
int n, m, k, ans;
bool v[MAXN][MAXN];
void dfs(int x, int y) {
if(x < 1 || y < 1 || x > n || y > m || !v[x][y]) return;
if(v[x-1][y] + v[x+1][y] < 2 && v[x][y-1] + v[x][y+1] < 2) {
++ans;
v[x][y] = 0;
dfs(x+1, y);
dfs(x, y+1);
dfs(x-1, y);
dfs(x, y-1);
}
}
int main () {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d%d", &n, &m, &k);
int x, y;
memset(v, 1, sizeof v);
while(k--) {
scanf("%d%d", &x, &y);
ans = 0;
if(v[x][y]) {
++ans;
v[x][y] = 0;
dfs(x+1, y);
dfs(x, y+1);
dfs(x-1, y);
dfs(x, y-1);
}
printf("%d\n", ans);
}
}
}
略
分治,考虑跨过最大值的区间。枚举较小的一边。预处理一些东西后就可以 O ( 1 ) O(1) O(1)查询固定一个端点的答案。
需要预处理的:
1.st表求区间最大值
2.每个位置往左和往右最远能走多少没有相同的数字
#include
using namespace std;
const int MAXN = 300005;
const int LOG = 19;
long long ans;
int Lg[MAXN], n, k, a[MAXN], L[MAXN], R[MAXN], lst[MAXN];
pair<int,int>f[MAXN][LOG];
inline int qmx(int l, int r) {
int t = Lg[r-l+1];
return max(f[l][t], f[r-(1<<t)+1][t]).second;
}
void solve(int l, int r) {
if(l > r) return;
int pos = qmx(l, r), Min = max(a[pos]-k, 1);
if(pos-l < r-pos) {
for(int i = l, rr, ll; i <= pos; ++i) {
rr = min(r, R[i]);
ll = max(pos, i+Min-1);
ans += ll <= rr ? rr-ll+1 : 0;
}
}
else {
for(int i = r, ll, rr; i >= pos; --i) {
ll = max(l, L[i]);
rr = min(pos, i-Min+1);
ans += ll <= rr ? rr-ll+1 : 0;
}
}
solve(l, pos-1);
solve(pos+1, r);
}
signed main () {
Lg[0] = -1;
for(int i = 1; i < MAXN; ++i) Lg[i] = Lg[i>>1] + 1;
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &k);
memset(lst, 0, sizeof lst);
int now = 1;
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]), f[i][0] = make_pair(a[i], i);
if(lst[a[i]]) now = max(now, lst[a[i]]+1);
L[i] = now;
lst[a[i]] = i;
}
for(int j = 1; j <= Lg[n]; ++j)
for(int i = 1; i+(1<<j)-1 <= n; ++i)
f[i][j] = max(f[i][j-1], f[i+(1<<(j-1))][j-1]);
memset(lst, 0, sizeof lst);
now = n;
for(int i = n; i >= 1; --i) {
if(lst[a[i]]) now = min(now, lst[a[i]]-1);
R[i] = now;
lst[a[i]] = i;
}
ans = 0; solve(1, n);
printf("%lld\n", ans);
}
}