ccpc网络赛也打残了,没有好好补题了x(我今天又好好的成为了一个辣鸡呢
upd:好难啊补不动了
1001
好神啊(不过qls的题解里面好几个错误,我被坑飞了,样例都过不了的那种。
用
f
i
,
j
f_{i,j}
fi,j表示只考虑权值
<
=
i
<=i
<=i的边,将
j
j
j个点联通的概率。
然后我们考虑下怎么转移,发现不会转移x
然后我们可以使用DP套DP。
设一个新的
g
t
,
i
,
j
g_{t, i, j}
gt,i,j表示只考虑
<
t
<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}
xt−1,这时候最后的关于
x
x
x的多项式中,
x
t
x^t
xt的系数就是最小生成树为
t
+
n
−
1
t+n-1
t+n−1的概率。
但是这个多项式我们不好直接求出来,我们可以取
(
k
−
1
)
∗
(
n
−
1
)
(k - 1) * (n - 1)
(k−1)∗(n−1)个
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';
}
}