[比赛][2017省队十连测推广赛1]
Pre()
这是BZOJ上打的第一场比赛,也是博客里写的第一篇比赛题解。。。
T1
题目大意:
给定一棵
N<=100000
个节点的有根树,设
sum[i]
是以
i
为根的子树的权值和。命令
思路:
一开始的想法是把树分块,发现并不可做。然后考虑对
sum
序列分块分成
N−−√
块,预处理
f[i][j]
为节点
j
的祖先中编号属于块
这样子再预处理一个
s[i]
表示第
i
块的
这个式子显然成立(一个节点的权值改变对它和它的父亲都有贡献为 y−x ,每个块的父亲数量之前已经预处理出来了)。
块和块整体的询问我们已经预处理好了,那么如果查询的时候除了块之间的还有整体块两旁不满一整块的一些点呢?
我们先处理一下
dfs
序把一棵树整理成序列,一个点及其子树在序列上肯定是连续的一段,单点修改的时候我们顺便把点
v
修改的时候每个块都要修改一遍,复杂度 O(N−−√) ,查询的时候块最多有 O(N−−√) 块,零散的点最多有 O(N−−√) 个。预处理时每个点都要 O(N−−√) 做一遍。
所以总复杂度是: O((N+M)N−−√)
坑:
树状数组两个操作都打错了。。。贡献了一页的RE。
还有如果询问的
L
要开 unsigned long long
代码:
#include <bits/stdc++.h>
const int Maxn = 100110;
typedef unsigned long long ll;
using namespace std;
inline char get(void) {
static char buf[1000000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 1000000, 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 read(ll &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(ll x) {
if (!x) return (void)puts("0");
if (x < 0) putchar('-'), x = -x;
static short s[60], t;
while (x) s[++t] = x % 10, x /= 10;
while (t) putchar('0' + s[t--]);
putchar('\n');
}
int n, m;
ll t[Maxn];
inline void insert(int x, long long v) {
for (; x < Maxn; x += (x & -x)) t[x] += v;
}
inline ll query(int x) {
ll ret = 0;
for (; x > 0; x -= (x & -x)) ret += t[x];
return ret;
}
int head[Maxn], sub;
struct Edge {
int to, nxt;
Edge(void) {}
Edge(const int &to, const int &nxt) : to(to), nxt(nxt) {}
} edge[Maxn << 1];
inline void add(const int &a, const int &b) {
edge[++sub] = Edge(b, head[a]), head[a] = sub;
}
ll sum[320], a[Maxn];
int bb[320][Maxn], st[Maxn], ed[Maxn];
int block, belong[Maxn], root, dfn[Maxn], en[Maxn], ff;
inline void dfs(int u, int fa) {
dfn[u] = ++ff;
bb[belong[u]][u]++;
for (int i = head[u], v; i; i = edge[i].nxt) {
v = edge[i].to;
if (fa == v) continue;
for (int k = 1; k <= block; k++)
bb[k][v] = bb[k][u];
dfs(v, u);
}
en[u] = ff;
}
int main(void) {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
read(n), read(m);
for (int i = 1; i <= n; i++) read(a[i]);
for (int i = 1; i <= n; i++) {
int a, b;
read(a), read(b);
if (!a || !b) {
root = a ^ b;
continue;
}
add(a, b), add(b, a);
}
block = sqrt(n) + 1;
for (int i = 1; i <= n; i++) {
belong[i] = (i - 1) / block + 1;
ed[belong[i]] = i;
if (!st[belong[i]]) st[belong[i]] = i;
}
dfs(root, 0);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= block; j++) {
sum[j] += bb[j][i] * a[i];
}
insert(dfn[i], a[i]);
}
int x, wa, al, bl;
ll y, ret;
//cout << "ok" << endl;
while (m--) {
read(wa), read(x), read(y);
if (wa == 2) {
ret = 0;
al = belong[x]; bl = belong[y];
if (al == bl) {
for (int i = x; i <= y; i++) ret += query(en[i]) - query(dfn[i] - 1);
write(ret);
continue;
}
for (int i = al + 1; i < bl; i++)
ret += sum[i];
for (int i = x; i <= ed[al]; i++) {
ret += query(en[i]) - query(dfn[i] - 1);
}
for (int i = st[bl]; i <= y; i++) {
ret += query(en[i]) - query(dfn[i] - 1);
}
write(ret);
} else {
for (int i = 1; i <= block; i++)
sum[i] = sum[i] + (long long) bb[i][x] * (y - a[x]);
insert(dfn[x], y - a[x]);
a[x] = y;
}
}
return 0;
}
T2
题目大意:
给定一个一边点数为
n
,另一边点数为
答案要
%p
思路:
好神啊并不会,机房某位大神一搜发现是结论题。
答案就是: (nm−1∗mn−1)%p
于是快速幂套快速乘就水过了。。。
复杂度貌似是 O(log2N)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL fast_multi(LL m, LL n, LL mod)
{
LL ans = 0;
while (n)
{
if (n & 1)
ans += m;
m = (m + m) % mod;
m %= mod;
ans %= mod;
n >>= 1;
}
return ans;
}
LL fast_pow(LL a, LL n, LL mod)
{
LL ans = 1;
while (n)
{
if (n & 1)
ans = fast_multi(ans, a, mod);
a = fast_multi(a, a, mod);
ans %= mod;
a %= mod;
n >>= 1;
}
return ans;
}
LL a, b, c;
int main(void) {
cin >> a >> b >> c;
cout << fast_multi(fast_pow(a, b - 1, c), fast_pow(b, a - 1, c), c) << endl;
return 0;
}
T3
题目大意:
给定两种移动方式 (u,v)−>(u+Ax,v+Ay) , (u,v)−>(u+Bx,v+By) ,求从 (0,0) 移动到 (Ex,Ey) 共有多少种方法。
要 %1000000007
思路:
考虑把从
(0,0)
移动到
(ex,ey)表示成
一个关于
(M,N)
(
M
表示用第一种方法的数量,
能从
(0,0)
移动到
(ex,ey)
,当且仅当
N
,
且对于确定的 (ex,ey) ,对应的 (M,N) 一定唯一(二元一次方程组的性质)。
这样子,我们可以把图重新建成一张坐标为 (M,N) 的网格图。
而从 (0,0) 移动到 (ex,ey) 的方案数就是 CMN+M 。
考虑
DP
+容斥,对求出网格图上的所有点按
N
为第一关键字,
状态转移方程就是:
处理排列数的时候要预处理一下阶乘和逆元。。。当然逆元像我一样打了快速幂也可以。。。
代码:
#include <bits/stdc++.h>
const int Maxn = 510;
const int Mod = 1000000007;
typedef long long ll;
using namespace std;
inline char get(void) {
static char buf[1000000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline void read(ll &x) {
x = 0; static char c; bool f = 0;
for (; !(c >= '0' && c <= '9'); c = get()) if (c == '-') f = 1;
for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); if (f) x = -x;
}
ll aX, aY, bX, bY, tot;
ll power(ll a, ll b) {
ll ret = 1;
while (b) {
if (b & 1) (ret *= a) %= Mod;
(a *= a) %= Mod;
b >>= 1;
}
return ret;
}
ll ff[1000001];
ll fab(ll n) {
return ff[n];
}
ll C(ll n, ll m) {
ll a = fab(n + m), b = fab(m), c = fab(n);
return a * power(b, Mod - 2) % Mod * power(c, Mod - 2) % Mod;
}
ll CC(ll n, ll m) {
if (n < 0 || m < 0) return 0;
else return C(n, m);
}
ll N, M, NN, MM;
inline void Clac(ll Ex, ll Ey) {
ll t1 = (bY * aX - bX * aY), t2 = (Ey * aX - Ex * aY);
if (t2 % t1 != 0) {
N = -1;
return;
}
N = t2 / t1;
t1 = (bX * aY - bY * aX), t2 = (Ey * bX - Ex * bY);
if (t2 % t1 != 0) {
M = -1;
return ;
}
M = t2 / t1;
if (M < 0 || N < 0) return;
}
ll n, f[Maxn];
struct Pos {
ll x, y;
friend bool operator < (const Pos &a, const Pos &b) {
if (a.x == b.x) return a.y < b.y;
return a.x < b.x;
}
} pos[Maxn];
int main(void) {
ll init = 1;
ff[0] = 1;
for (int i = 1; i <= 1000000; i++) {
init = (init * i) % Mod;
ff[i] = init;
}
ll Ex, Ey, tot = 0;
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
read(Ex), read(Ey), read(n);
read(aX), read(aY), read(bX), read(bY);
Clac(Ex, Ey); MM = M, NN = N;
ll x, y;
for (ll i = 1; i <= n; i++) {
read(x), read(y);
Clac(x, y);
if (N < 0 || M < 0 || N > NN || M > MM) continue;
tot++;
pos[tot].x = N, pos[tot].y = M;
}
tot++;
pos[tot].x = NN, pos[tot].y = MM;
sort(pos + 1, pos + 1 + tot);
for (ll i = 1; i <= tot; i++) {
f[i] = CC(pos[i].x, pos[i].y);
for (ll j = 1; j < i; j++)
if (pos[j].y <= pos[i].y)
f[i] = (f[i] + Mod - (CC(pos[i].x - pos[j].x, pos[i].y - pos[j].y) * f[j]) % Mod) % Mod;
}
for (ll i = tot; i; i--) {
if (f[i]) {
cout << f[i] << endl;
return 0;
}
}
cout << 0 << endl;
return 0;
}
撒花~