2019 hdu多校第十场 7/11

ccpc网络赛也打残了,没有好好补题了x(我今天又好好的成为了一个辣鸡呢
upd:好难啊补不动了

1001

好神啊(不过qls的题解里面好几个错误,我被坑飞了,样例都过不了的那种。
f i , j f_{i,j} fi,j表示只考虑权值 &lt; = i &lt;=i <=i的边,将 j j j个点联通的概率。
然后我们考虑下怎么转移,发现不会转移x
然后我们可以使用DP套DP。
设一个新的 g t , i , j g_{t, i, j} gt,i,j表示只考虑 &lt; t &lt;t <t的边,那么就会有若干个连通块,从1号点所在的连通块大小为 i i i,用了权值为 t t t的边若干条,构成的新的1号点所在的连通块大小为 j j j,那么我们每次转移就直接背包进一个新的大小为 s s s的连通块进去。
此时你会发现 f i , s f_{i,s} fi,s是可以用 g g g处理的了,然后你就可以用 f i , s f_{i,s} fi,s帮助你计算 g g g数组转移的系数。
然后呢我们再多考虑上最小生成树这个条件,显然我们每次用至少有 t t t权值,并且一定有 t t t权值的边联通了两个连通块,那么每背包一个连通块进去,我们就要乘上 x t − 1 x^{t - 1} xt1,这时候最后的关于 x x x的多项式中, x t x^t xt的系数就是最小生成树为 t + n − 1 t+n-1 t+n1的概率。
但是这个多项式我们不好直接求出来,我们可以取 ( k − 1 ) ∗ ( n − 1 ) (k - 1) * (n - 1) (k1)(n1) x x x的值,然后拉格朗日插值把多项式求出来。
顺手偷了一个qls的拉格朗日插值板子x(只有分治ntt快速插值的人哭了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <utility>

using namespace std;

#define SZ(x) ((int)(x).size())

using ll = long long;

int const mod = 1e9 + 7;
int const N = 42;
int const M = 15;

ll KSM(ll a, ll k) {
	ll ret = 1;
	for (; k; k >>= 1, a = a * a % mod)
		if (k & 1)
			ret = ret * a % mod;
	return ret;
}

vector<int> interpolation(int* y, int n) {
	vector<int> u = vector<int>(y, y + n), ret = vector<int>(n), sum = vector<int>(n);
	ret[0] = u[0], sum[0] = 1;
	for (int i = 1; i < n; ++i) {
		for (int j = n - 1; j >= i; --j)
			u[j] = 1ll * (u[j] - u[j - 1] + mod) * KSM(i, mod - 2) % mod;
		for (int j = i; j > 0; --j) {
			sum[j] = (sum[j - 1] - 1ll * (i - 1) * sum[j] % mod + mod) % mod;
			ret[j] = (ret[j] + 1ll * sum[j] * u[i]) % mod;
		}
		sum[0] = 1ll * (i - 1) * (mod - sum[0]) % mod;
		ret[0] = (ret[0] + 1ll * sum[0] * u[i]) % mod;
	}
	return ret;
}

int f[M][N], g[N][N];
int tgr[M][N][N], teq[M][N][N];
int y[N * M];
int n, m;
int p[N], su[N];
int C[N][N], inv[N];

void init() {
	C[0][0] = 1;
	for (int i = 1; i < N; ++i) {
		C[i][0] = 1;
		for (int j = 1; j < N; ++j) 
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
	}
	inv[0] = 1;
	for (int i = 1; i < N; ++i)
		inv[i] = 1ll * inv[i - 1] * i % mod;
	inv[N - 1] = KSM(inv[N - 1], mod - 2);
	for (int i = N - 2; i >= 1; --i)
		inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
}

int main() {
	ios::sync_with_stdio(0);
	init();
	int T;
	cin >> T;
	while (T--) {
		cin >> n >> m;
		for (int i = 0; i <= m; ++i) {
			cin >> p[i];
			p[i] = 1ll * p[i] * KSM(100, mod - 2) % mod;
		}
		su[m + 1] = 0;
		for (int i = m; i >= 0; --i) 
			su[i] = (su[i + 1] + p[i]) % mod;
		for (int t = 1; t <= m; ++t)
			for (int i = 0; i <= n; ++i)
				for (int j = 0; i + j <= n; ++j) {
					tgr[t][i][j] = KSM(p[0] + su[t + 1], i * j);
					teq[t][i][j] = (KSM(p[0] + su[t], i * j) - tgr[t][i][j] + mod) % mod;
				}
		for (int x = 0; x <= (n - 1) * (m - 1); ++x) {
			for (int i = 0; i <= m; ++i)
				for (int j = 0; j <= n; ++j)
					f[i][j] = 0;
			f[0][1] = 1;
			int xp = 1;
			for (int t = 1; t <= m; ++t, xp = 1ll * xp * x % mod) {
				for (int i = 0; i <= n; ++i) {
					g[i][0] = 1;
					for (int j = 1; j <= n; ++j)
						g[i][j] = 0;
				}
				for (int s = 1; s <= n; ++s) {
					//f[t][s] = 0;
					for (int i = 1; i <= s; ++i)
						f[t][s] = (f[t][s] + 1ll * f[t - 1][i] * g[i][s - i] % mod * C[s - 1][i - 1] % mod) % mod;
					for (int i = 1; i <= n; ++i)
						for (int j = n - i; j >= 0; --j) {
							int t1 = 1ll * f[t][s] * teq[t][i][s] % mod * xp % mod;
							int t2 = 1;
							for (int k = 1; i + j + k * s <= n; ++k) {
								t2 = 1ll * t2 * t1 % mod * tgr[t][j + (k - 1) * s][s] % mod * C[j + k * s][s] % mod;
								g[i][j + k * s] += 1ll * g[i][j] * t2 % mod * inv[k] % mod;
								g[i][j + k * s] %= mod;
							}
						}
				}
			}
			y[x] = f[m][n];
		}
		auto res = interpolation(y, (m - 1) * (n - 1) + 1);
		cout << res[0];
		for (int i = 1; i < SZ(res); ++i)
			cout << ' ' << res[i];
		cout << '\n';
	}
}

1002

1003

可以发现小于0.5以下的不断组合可以让数更大,但是大到某种程度的时候就不能再继续变大了,而且显然最开始就用比较大的去组合,答案也会比较大,所以可以直接贪心从最大的开始选,如果选能让答案变大就变大。
话说cin无论关不关同步,读浮点数是真的慢,scanf就0.1s,cin就2s以上直接T飞

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <queue>
#include <iomanip>

using namespace std;

int const N = 10005;

double a[N];

int main() {
	freopen("a.in", "r", stdin);
	freopen("z.out", "w", stdout);
	ios::sync_with_stdio(0);
	int T;
	scanf("%d", &T);
	while (T--) {
		int n;
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i)
			scanf("%lf", &a[i]);
		sort(a + 1, a + n + 1);
		double ans = a[n];
		for (int i = n; i >= 2; --i)
			if (a[i] <= 0.5) {
				double anss = 0, c = 1;
				for (int j = i; j >= 1; --j) 
					if (anss * (1 - a[j]) + a[j] * c > anss) { 
						anss = anss * (1 - a[j]) + a[j] * c;
						c = c * (1 - a[j]);
					}
				ans = max(ans, anss);
				break;
			}
		printf("%.13f\n", ans);
	}
}

1004

首先我们肯定可以用SAM处理出每个位置的子串,他对应的所有可选的堆数是多少,然后但是葫芦要赢的话其实只要选一堆出来,那么一定可以赢(因为异或和非0),所以一定是必胜的,现在就只用考虑怎么选会让答案最大,因为线性基是个拟阵,所以贪心就行了,就是每次选最大的往里面插,能插就插,这个证明和MST的证明是一样的。
那么我们就先掏出所有可选的石子就行了,这些石子就是每个SAM节点在parent树上的子树,于是我们可以先取出所有的数,然后从大到小排序,每次暴力往上插到不能再插为止就行。
复杂度显然是 O ( n l o g n + 58 n ) O(nlogn + 58n) O(nlogn+58n)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cassert>
#include <utility>

using namespace std;

#define SZ(x) ((int)(x).size())

typedef long long ll;
typedef unsigned long long ull;

int const N = 200005;
int const LOGN = 21;

struct L_B{
    long long d[61], p[61];
	ull ans;

    L_B() {
        memset(d, 0, sizeof(d));
        memset(p, 0, sizeof(p));
        ans = 0;
    }

    bool insert(long long val) {
		auto v = val;
        for (int i = 60; i >= 0; --i)
            if (val & (1LL << i)) {
                if (d[i] == 0) {
					ans += v;
                    d[i] = val;
                    break;
                }
                val ^= d[i];
            }
        return val > 0;
    }
} lb[N];

int ch[N][26], sam_sz[N], par[N], mx[N];
int sam_cnt, sam_last;
ll w[N];
int fa[N][LOGN];
int pos[N];
int n;
string s;


void SAM_init() {
	sam_cnt = sam_last = 1;
	memset(ch, 0, sizeof(ch));
	memset(par, 0, sizeof(par));
	memset(mx, 0, sizeof(mx));
	memset(sam_sz, 0, sizeof(sam_sz));
}

int SAM_extend(int x) {
	int p, q, np, nq;
	p = sam_last;
	sam_last = np = ++sam_cnt;
	mx[np] = mx[p] + 1;
	sam_sz[np] = 1;
	for(; p && !ch[p][x]; p = par[p]) ch[p][x] = np;
	if(!p) par[np] = 1;
	else {
		q = ch[p][x];
		if(mx[q] == mx[p] + 1) par[np] = q;
		else {
			nq = ++sam_cnt;
			mx[nq] = mx[p] + 1;
			memcpy(ch[nq], ch[q], sizeof(ch[q]));
			par[nq] = par[q];
			par[q] = par[np] = nq;
			for(; ch[p][x] == q; p = par[p]) ch[p][x] = nq;
		}
	}
	return sam_last;
}

int samt[N];
void SAM_topoSort() {
	static int b[N];
	memset(b, 0, sizeof(b));
	memset(samt, 0, sizeof(samt));
	for(int i = 1; i <= sam_cnt; i++) b[mx[i]]++;
	for(int i = 1; i <= sam_cnt; i++) b[i] += b[i - 1];
	for(int i = sam_cnt; i >= 1; i--) samt[b[mx[i]]--] = i;
	for(int i = sam_cnt; i >= 1; i--) sam_sz[par[samt[i]]] += sam_sz[samt[i]];
	for (int i = 1; i <= sam_cnt; ++i) {
		fa[samt[i]][0] = par[samt[i]];
		for (int j = 1; j < LOGN; ++j)
			fa[samt[i]][j] = fa[fa[samt[i]][j - 1]][j - 1];
	}
}

bool cmp(pair<ll, int> const& x, pair<ll, int> const& y) {
	if (x.first == y.first)
		return x.second > y.second;
	return x.first > y.first;
}

int main() {
	ios::sync_with_stdio(0);
	int T;
	cin >> T;
	while (T--) {
		cin >> n;
		cin >> s;
		SAM_init();
		for (int i = 1; i <= n; ++i)
			cin >> w[i];
		for (int i = 0; i < SZ(s); ++i) {
			SAM_extend(s[i] - 'a');
			pos[i + 1] = sam_last;
		}
		SAM_topoSort();
		auto det = vector<pair<ll, int>>();
		for (int i = 1; i <= sam_cnt; ++i) {
			lb[i] = L_B();
			det.push_back(make_pair(w[sam_sz[i]], i));
		}
		//sort(det.begin(), det.end(), greater<pair<ll, int>>());
		sort(det.begin(), det.end(), cmp);
		for (auto const& pr : det) 
			for (int x = pr.second; x; x = fa[x][0]) 
				if (lb[x].insert(pr.first) == 0)
					break;
		int m;
		cin >> m;
		while (m--) {
			int l, r;
			cin >> l >> r;
			int p = pos[r];
			for (int i = LOGN - 1; i >= 0; --i)
				if (mx[fa[p][i]] >= r - l + 1) 
					p = fa[p][i];
			cout << lb[p].ans << '\n';
		}
	}
}

1005

来自小洛洛

#include <iostream>
#include <set>
#include <cstdint>
#include <vector>
#include <utility>
#include <algorithm>
#include <functional>
#include <limits>
#include <cstdio>

using i64 = int64_t;
using Pr = std::pair<i64, i64>;

const i64 INF = std::numeric_limits<i64>::max();

i64 solve (std::vector<Pr> &v) {
    std::sort(std::begin(v), std::end(v), std::greater<Pr>());
    int n = v.size();

    auto mxs = std::vector<i64>(n);
    i64 mx = ~INF;
    for (int i = 0; i < n; ++i) {
        mxs[i] = mx; // max [0, i)
        mx = std::max(mx, v[i].second);
    }

    std::set<i64> s;
    i64 ans = INF;
    for (int i = n - 1; i >= 0; --i) {
        i64 s1 = v[i].first;
        i64 candi = mxs[i];
        if (candi != ~INF)
            ans = std::min(ans, std::abs(s1 - candi));

        auto it = s.lower_bound(s1);
        if (it != s.end() && *it > candi)
            ans = std::min(ans, std::abs(s1 - *it));
        if (it != s.begin() && *--it > candi)
            ans = std::min(ans, std::abs(s1 - *it));

        s.insert(v[i].second);
    }

    return ans;
}

int main () {
    int t;
    // std::cin >> t;
    scanf("%d", &t);

    while (t--) {
        int n;
        // std::cin >> n;
        scanf("%d", &n);

        auto v = std::vector<Pr>(n);
        for (auto &c: v)
            // std::cin >> c.first >> c.second;
            scanf("%lld%lld", &c.first, &c.second);

        auto ans = solve(v);
        // std::cout << ans << '\n';
        printf("%lld\n", ans);
    }

    return 0;
}

1006

1007

1008

模拟网络流的退流操作,每次考虑从某个位置往回改,不断贪心即可。

#include <iostream>
#include <cstdint>
#include <vector>
#include <utility>
#include <algorithm>
#include <limits>
#include <cstdio>
#include <queue>

using Pr = std::pair<int, int>;
const int INF = std::numeric_limits<int>::max();

template<typename T>
class Heap {
    mutable std::priority_queue<T> q, q_del;

    void maintain () const {
        while (!q_del.empty() && q_del.top() == q.top()) {
            q.pop();
            q_del.pop();
        }
    }

public:
    Heap() {}

    void push (T val) {
        q.push(val);
    }

    void remove (T val) {
        q_del.push(val);
    }

    void pop () {
        maintain();
        q.pop();
    }

    bool empty () const {
        maintain();
        return q.empty();
    }

    const T &top () const {
        maintain();
        return q.top();
    }
};

std::vector<int> solve (std::vector<Pr> &v) {
    int n = v.size();
    auto sel = std::vector<char>(n, 0);

    auto q0 = Heap<Pr>();
    auto qrev = Heap<Pr>();
    auto qcandi = Heap<Pr>();
    for (int i = 0; i < n; ++i) {
        q0.push({ v[i].first, i });
        qcandi.push({ v[i].first + v[i].second, i });
    }

    auto ans = std::vector<int>(n * 2);
    int cur = 0;
    for (int i = 0; i < n * 2; ++i) {
        int c1 = q0.empty() ? ~INF : q0.top().first;
        int c2 = qcandi.empty() || qrev.empty() ? ~INF
            : qcandi.top().first + qrev.top().first;
        // std::cerr << "> " << c1 << ' ' << c2 << '\n';

        if (c1 > c2) {
            cur += c1;
            // std::cerr << "fst " << q0.top().second << "\n";

            int idx = q0.top().second;
            if (++sel[idx] == 1) { // 0 -> 1
                // un0
                q0.pop();
                qcandi.remove({ v[idx].first + v[idx].second, idx });
                // to1
                qrev.push({ -v[idx].first, idx });
                q0.push({ v[idx].second, idx });
            } else { // 1 -> 2
                // un1
                q0.pop();
                qrev.remove({ -v[idx].first, idx });
                // to2
                qrev.push({ -v[idx].second, idx });
            }
        } else {
            cur += c2;
            // std::cerr << "chg " << qrev.top().second << " -> " << qcandi.top().second << "\n";

            int idx_rev = qrev.top().second;
            int idx = qcandi.top().second;
            // link>>
            qcandi.pop();

            if (--sel[idx_rev] == 1) { // 2 -> 1
                // un2
                qrev.pop();
                // to1
                q0.push({ v[idx_rev].second, idx_rev });
                qrev.push({ -v[idx_rev].first, idx_rev });
            } else { // 1 -> 0
                // un1
                qrev.pop();
                q0.remove({ v[idx_rev].second, idx_rev });
                // to0
                q0.push({ v[idx_rev].first, idx_rev });
                qcandi.push({ v[idx_rev].first + v[idx_rev].second, idx_rev });
            }

            sel[idx] += 2;
            // 0 -> 2
            // un0
            // link <<
            q0.remove({ v[idx].first, idx });
            // to2
            qrev.push({ -v[idx].second, idx });
        }

        ans[i] = cur;
    }

    return ans;
}

int main () {
    // std::ios::sync_with_stdio(false);

    int t;
    scanf("%d", &t);

    while (t--) {
        int n;
        scanf("%d", &n);

        auto v = std::vector<Pr>(n);
        for (auto &c: v)
            scanf("%d%d", &c.first, &c.second);

        auto ans = solve(v);
        for (auto c: ans)
            printf("%d ", c);
        putchar('\n');
    }

    return 0;
}

1009

大力BFS就好了

#include <bits/stdc++.h>
#include <sys/timeb.h>
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define USE_CIN_COUT ios::sync_with_stdio(0)
#define filein(x) freopen(#x".in","r",stdin)
#define fileout(x) freopen(#x".out","w",stdout)
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
#define mkd(x) freopen(#x".in","w",stdout);

using namespace std;

int random(int l, int r) {
    static std::random_device rd;
    struct timeb timeSeed;
    ftime(&timeSeed);
    size_t seed = timeSeed.time * 1000 + timeSeed.millitm;  // milli time
    static std::mt19937 gen(seed);
    std::uniform_int_distribution<> u(l, r);
    return u(gen);
}

typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const int maxn = 2010;
bool vis[maxn][maxn];
int n,m;

int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};

bool chk(int x, int y) {
    if (vis[x - 1][y] && vis[x + 1][y])
        return 0;
    if (vis[x][y - 1] && vis[x][y + 1])
        return 0;
    return 1;
}

struct node {
    int x, y;
};

int bfs(int x,int y) {
    if (vis[x][y] == 0)
        return 0;
    int ret = 0;
    auto q = queue<node>();
    if (vis[x][y] == 1) {
        q.push({ x, y });
        vis[x][y] = 0;
        ++ret;
    }
    while (q.empty() == 0) {
        auto now = q.front();
        q.pop();
        for(int i = 0;i < 4;i++) {
            int nx = now.x+dx[i];
            int ny = now.y+dy[i];
            if(0 < nx && nx <= n && 0 < ny && ny <= m && vis[nx][ny] && chk(nx, ny)) {
                q.push({ nx, ny });
                vis[nx][ny] = 0;
                ++ret;
            }
        }
    }
    return ret;
}

int main() {
    USE_CIN_COUT;
    int T;
    cin >> T;
    while(T--) {
        int q;
        cin >> n >> m >> q;
        if (n < 1 || n > 2000)
            while (1);
        if (m < 1 || m > 2000)
            while (1);
        for(int i = 0;i <= n + 1; i++) 
            for(int j = 0;j <= m + 1;j++) 
                vis[i][j] = 1;
        while (q--) {
            int x, y;
            cin >> x >> y;
            if (x <= 0 || x > n || y <= 0 || y > m)
                while (1);
            cout << bfs(x, y) << '\n';
        }
    }
    return 0;
}

1010

1011

直接笛卡尔树上分治,最开始搞出每个位置往左往右最多到哪里,然后每次分治枚举一个端点,那么就可以得到另一个端点可选的区间,然后就完事了,友好的 O ( n l o g n ) O(nlogn) O(nlogn)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;

using ll = long long;

inline ll read() {
    ll d = 0;
    char s = getchar();
    while (s < '0' || s > '9') 
        s = getchar();
    while ('0' <= s && s <= '9') {
        d = d * 10 + s - '0';
        s = getchar();
    }
    return d;
}

int const N = 300005;

int a[N];
int f[N][21];
ll ans;
int n, m;
int pre[N], lef[N], rig[N];
 
void DP() {
    for (int i = 1; i <= n; ++i)
        f[i][0] = i;
    for (int j = 1; (1 << j) <= n; ++j)
        for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
            int x = f[i][j - 1], y = f[i + (1 << (j - 1))][j - 1];
            f[i][j] = a[x] < a[y] ? y : x;
        }
}

int RMQ(int l, int r) {
    int k = 0;
    while ((1 << (k + 1)) <= r - l + 1) 
        ++k;
    int x = f[l][k], y = f[r - (1 << k) + 1][k];
    return a[x] > a[y] ? x : y;
}

void build(int l, int r) {
    if (l == r) {
        if (a[l] - 1 <= m)
            ++ans;
        return;
    }
    if (l > r)
        return;
    int mid = RMQ(l, r);
    build(l, mid - 1);
    build(mid + 1, r);
    if (mid - l + 1 <= r - mid) {
        for (int i = l; i <= mid; ++i) {
            int mir = a[mid] + i - 1 - m;
            if (min(rig[i], r) < max(mir, mid))
                continue;
            ans += min(rig[i], r) - max(mir, mid) + 1;
        }
    } else {
        for (int i = mid; i <= r; ++i) {
            int mil = m - a[mid] + i + 1;
            if (max(lef[i], l) > min(mil, mid))
                continue;
            ans += min(mil, mid) - max(lef[i], l) + 1;
        }
    }
}

int main() {
    int T = read();
    while (T--) {
        n = read(), m = read();
        for (int i = 1; i <= n; ++i)
            pre[i] = 0;
        for (int i = 1; i <= n; ++i) {
            a[i] = read();
            lef[i] = max(lef[i - 1], pre[a[i]] + 1);
            pre[a[i]] = i;
        }
        for (int i = 1; i <= n; ++i)
            pre[i] = n + 1;
        rig[n + 1] = n + 1;
        for (int i = n; i >= 1; --i) {
            rig[i] = min(rig[i + 1], pre[a[i]] - 1);
            pre[a[i]] = i;
        }

        DP();
        ans = 0;
        build(1, n);
        cout << ans << '\n';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值