您的位置:首页 > 其它

[比赛][Zjoi2017 Day1]

2017-03-28 20:58 246 查看

[比赛][Zjoi2017 Day1]

ZJOI2017 DAY1

为什么第一题就是仙人掌啊吓死人了。。。

刚看完题发现只会打10分的暴力,然后看了第二题发现还是只会打10分的暴力,然后看了第三题还是只会十分的暴力。。。

感觉要30分滚粗了。。。

先写好了第一题的暴力,调了一会(其实调了一个多小时)过了样例以后发现n哪怕再大1都跑不过。。。于是就跑了一下链的情况,发现答案就是2n−2,好开心啊

又想了一会树的情况,发现树的情况就是问把一棵树拆成很多条链(链的头尾相连可以连成仙人掌),问又多少种情况,然后大力DP一下,但是转移的时候要乘个系数并不会,瞎JB乱乘了一个当然是错的。

打完还有半个小时多一点,把二三两题的暴力打完就滚粗了。。。。

然后来讲一下一二两题的做法。

仙人掌

可以看一下这篇题解:http://blog.csdn.net/akak__ii/article/details/65935711

代码:

#include <bits/stdc++.h>
using namespace std;
const int Maxn = 500010;
const int Mod = 998244353;
typedef long long ll;
inline char get(void) {
static char buf[100000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline void read(int &x) {
x = 0; static char c;
for (; !(c >= '0' && c <= '9'); c = get());
for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get());
}
ll f[Maxn];
int head[Maxn], sub, d[Maxn], dfn[Maxn], cnt, fa[Maxn], rev[Maxn];

struct Edge {
int to, nxt;
Edge (void) {}
Edge (const int &to, const int &nxt) : to(to), nxt(nxt) {}
} edge[1000010];
inline void add(int a, int b) {
edge[++sub] = Edge(b, head[a]), head[a] = sub;
}
inline bool dfs(int u) {
dfn[u] = ++cnt;
for (int i = head[u], v; i; i = edge[i].nxt) {
v = edge[i].to;
if (!dfn[v]) {
fa[v] = u; if (dfs(v)) return 1;
} else if (dfn[v] > dfn[u])
for (d[u] -= 2; v != u; rev[v] = 1, d[v] -= 2, v = fa[v])
if (rev[v]) return 1;
}
return 0;
}
int T, n, m;
int main(void) {
//freopen("in.txt", "r", stdin);
register int i; int a, b;
for (f[0] = f[1] = 1, i = 2; i < Maxn; i++) f[i] = (f[i - 2] * (i - 1) % Mod + f[i - 1]) % Mod;
read(T);
while (T--) {
read(n); read(m); sub = cnt = 0;
for (int i = 1; i <= n; i++) head[i] = d[i] = dfn[i] = rev[i] = 0;
for (int i = 1; i <= m; i++) {
read(a), read(b); d[a]++, d++;
add(a, b), add(b, a);
}
if (dfs(1)) {
puts("0");
continue;
}
ll ans = 1;
for (int i = 1; i <= n; i++) ans = (ans * f[d[i]]) % Mod;
printf("%lld\n", ans);
}
return 0;
}


[b]树状数组

可以发现劼司机打错的树状数组求的是后缀和。回答错误当且仅当 x-1 的修改次数模 2 意义下不等于 y 的修改次数(即和为奇数)。

所以把询问看作点 (x-1, y),我们用树套树来维护这个平面。

对于修改操作 (x, y),它会对三种询问点产生影响:若询问点只包含 x 或只包含 y,那么有 1/(y-x+1) 的几率反色;若两个都包含,则 2/(y-x+1) 的几率反色。

若当前为 0 的概率是 x,然后有 p 的几率反色,则 x 变成 x*(1-p)+(1-x)*p。此式也适用于标记合并。

然后学习了一下如何在区间上打永久标记

代码:

#include <bits/stdc++.h>
using namespace std;
const int Maxn = 400010;
const int Maxm = 40000000;
const int Mod = 998244353;
typedef long long ll;

inline char get(void) {
static char buf[100000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}

inline void read(int &x) {
x = 0; static char c;
for (; !(c >= '0' && c <= '9'); c = get());
for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get());
}

inline void write(int x) {
if (!x) return (void) (puts("0"));
static short st[12], top;
while (x) st[++top] = x % 10, x /= 10;
while (top) putchar('0' + st[top--]);
putchar('\n');
}
int Pow(ll a, int n)
{
ll ans = 1;
while (n)
{
if (n & 1) ans = (ans * a) % Mod;
a = (a * a) % Mod;
n >>= 1;
}
return ans;
}
inline int merge(int a, int b) {
return (1LL * a * (Mod + 1 - b) % Mod + 1LL * b * (Mod + 1 - a) % Mod) % Mod;
}

int t2[Maxm], sz, ls[Maxm], rs[Maxm], rt[Maxn], n, m;
inline void add2(int &o, int l, int r, int L, int R, int v) {
if (!o) o = ++sz;
if (L == l && R == r) {
t2[o] = merge(t2[o], v);
return ;
}
int mid = (l + r) >> 1;
if (mid >= R) add2(ls[o], l, mid, L, R, v);
else if (mid < L) add2(rs[o], mid + 1, r, L, R, v);
else add2(ls[o], l, mid, L, mid, v), add2(rs[o], mid + 1, r, mid + 1, R, v);
}

inline void add1(int o, int l, int r, int L, int R, int L1, int R1, int v) {
if (l == L && r == R) {
add2(rt[o], 0, n, L1, R1, v);
return ;
}
int mid = (l + r) >> 1;
if (mid >= R) add1(o << 1, l, mid, L, R, L1, R1, v);
else if (mid < L) add1(o << 1 | 1, mid + 1, r, L, R, L1, R1, v);
else add1(o << 1, l, mid, L, mid, L1, R1, v), add1(o << 1 | 1, mid + 1, r, mid + 1, R, L1, R1, v);
}

inline int query2(int o, int l, int r, int p) {
if (!o) return 0;
int ret = t2[o];
if (l == r) return ret;
int mid = (l + r) >> 1;
if (p <= mid) return merge(query2(ls[o], l, mid, p), ret);
else return merge(query2(rs[o], mid + 1, r, p), ret);
}

inline int query1(int o, int l, int r, int p1, int p2) {
int ret = query2(rt[o], 0, n, p2);
if (l == r) {
return ret;
}
int mid = (l + r) >> 1;
if (p1 <= mid) return merge(query1(o << 1, l, mid, p1, p2), ret);
else return merge(query1(o << 1 | 1, mid + 1, r, p1, p2), ret);
}

int main(void) {
//freopen("in.txt", "r", stdin);

read(n), read(m); n++; int op, l, r, inv, tot, len;
for (int i = 1; i <= m; i++) {
read(op), read(l), read(r); len = r - l + 1, inv = Pow(len, Mod - 2);

if (op == 1) {
add1(1, 0, n, l, r, l, r, 2LL * inv % Mod);
add1(1, 0, n, 0, l - 1, l, r, inv);
add1(1, 0, n, l, r, r + 1, n, inv);
tot++;
} else {
if (l == 1 && (tot & 1)) write(query1(1, 0, n, 0, r));
else write((Mod + 1 - query1(1, 0, n, l - 1, r)) % Mod);
}
}
return 0;
}


ZJOI2018加油咯

或者二试翻盘
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  oi zjoi