T1
题意
给出
N
N
N,求形如
(
a
,
b
,
c
,
d
)
(a,b,c,d)
(a,b,c,d)的有序数对使得
a
+
b
+
c
+
d
=
N
a+b+c+d=N
a+b+c+d=N且
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d均为质数。
N
≤
100000
N\le 100000
N≤100000
题解
考场上我就写了 F F T FFT FFT,然后过掉了。
然后发现 100000 100000 100000内的质数大概只有 9500 9500 9500个,然后加起来的和 ≤ 100000 \le100000 ≤100000的质数对是可以枚举的。所以直接先预处理每个数等于两个质数加起来的方案,然后枚举前两个数的和 x x x,乘上 N − x N-x N−x等于后两个数的和的方案,最后全部加起来就是了。
放上 F F T FFT FFT的代码
CODE
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long double ld;
typedef double db;
const int MAXN = 100005;
const db Pi = acos(-1.0);
int p[MAXN/10], cnt;
bool vis[MAXN];
void pre(int N) {
for(int i = 2; i <= N; ++i) {
if(!vis[i]) p[++cnt] = i;
for(int j = 1; j <= cnt && i*p[j] <= N; ++j) {
vis[i*p[j]] = 1;
if(i % p[j] == 0) break;
}
}
}
const int MAXL = (1<<18)+5;
struct cp {
db x, y;
cp(){}
cp(db x, db y):x(x), y(y){}
inline cp operator +(const cp &o)const { return cp(x+o.x, y+o.y); }
inline cp operator -(const cp &o)const { return cp(x-o.x, y-o.y); }
inline cp operator *(const cp &o)const { return cp(x*o.x - y*o.y, x*o.y + o.x*y); }
}f[MAXL], tmp[2][19][MAXL/2];
int rv[MAXL];
inline void FFT(cp* arr, const int &n, const int &flg) {
for(register int i = 0; i < n; ++i) if(i < rv[i]) swap(arr[i], arr[rv[i]]);
static cp t;
for(register int i = 2, p = 1; i <= n; i<<=1, ++p) {
for(register int j = 0; j < n; j+=i) {
for(register int k = j, q = 0; k < j+i/2; ++k, ++q) {
t = arr[k + i/2] * tmp[flg][p][q];
arr[k + i/2] = arr[k] - t;
arr[k] = arr[k] + t;
}
}
}
if(!flg) for(register int i = 0; i < n; ++i) arr[i].x /= n;
}
int nn[15];
int main () {
freopen("plus.in", "r", stdin);
freopen("plus.out", "w", stdout);
int n = 0, T;
scanf("%d", &T);
for(int i = 1; i <= T; ++i)
scanf("%d", &nn[i]), n = max(n, nn[i]);
pre(n);
for(int i = 1; i <= cnt; ++i) f[p[i]] = cp(1, 0);
int len = 1; while(len <= (n<<1)) len<<=1;
for(int i = 1; i < len; ++i)
rv[i] = (rv[i>>1]>>1)|((i&1)*(len>>1));
for(int i = 1; i <= 18; ++i) {
cp w1 = cp(cos(2*Pi/(1<<i)), sin(2*Pi/(1<<i)));
cp w0 = cp(cos(-2*Pi/(1<<i)), sin(-2*Pi/(1<<i)));
tmp[1][i][0] = tmp[0][i][0] = cp(1, 0);
for(int j = 1; j <= (1<<17); ++j)
tmp[1][i][j] = tmp[1][i][j-1] * w1,
tmp[0][i][j] = tmp[0][i][j-1] * w0;
}
FFT(f, len, 1);
for(int i = 0; i < len; ++i) f[i] = f[i] * f[i];
FFT(f, len, 0);
for(int i = 1; i <= n; ++i) f[i] = cp(round(f[i].x), 0);
for(int i = n+1; i < len; ++i) f[i] = cp(0, 0);
FFT(f, len, 1);
for(int i = 0; i < len; ++i) f[i] = f[i] * f[i];
FFT(f, len, 0);
for(int i = 1; i <= T; ++i)
printf("%lld\n", (long long)(round(f[nn[i]].x)));
}
T2
题意
假设给定了两个整数 m , n m,n m,n。有 n n n 个互不相同的整数 x 1 , x 2 , . . . , x n ( 0 ≤ x i ≤ 2 m − 1 ) x1,x2,...,xn(0≤xi≤2^m-1) x1,x2,...,xn(0≤xi≤2m−1)。对于每一个属于 0 0 0 到 2 m − 1 2^m-1 2m−1 的 y,我们找到 p y p_y py使得 x p y x_{p_y} xpy异或 y y y 有最大值。即对于任意的 i = ̸ p y i=\not p_y i≠py,有 y ⊕ x p y > y ⊕ x i y⊕x_{p_y} >y⊕ x_i y⊕xpy>y⊕xi。(其中 ⊕ ⊕ ⊕表示二进制异或)。
现在我们把这个问题反过来。给定 m m m 和 n n n,以及序列 p 0 , p 1 , . . . , p 2 m − 1 p_0,p_1,...,p_{2^m-1} p0,p1,...,p2m−1,计算有多少个不同序列 x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn 可以通过上文描述的问题生成出序列 p p p。两个序列是不同的当且仅当存在一个 i 使得两个序列中 x i x_i xi是不同的。
答案对
1000000007
(
1
0
9
+
7
)
1000000007(10^9+7)
1000000007(109+7)取模。
m
≤
16
,
n
≤
2
m
m\le16,n\le 2^m
m≤16,n≤2m
题解
考虑分治。对于区间 ( l , r ) (l,r) (l,r)的所有 p p p考虑,这里的 p l . . . r p_{l...r} pl...r都要在 ( l , r ) (l,r) (l,r)范围内。
假设 x x x序列中既有最高位为 0 0 0的数,又有最高位为 1 1 1的数。那么 ( l , m i d ) (l,mid) (l,mid)的 p p p与 ( m i d + 1 , r ) (mid+1,r) (mid+1,r)的 p p p一定没有交集。因为他们的 p p p都是在对方的区间取得。那么就可以分治下去。方案就是 ( l , m i d ) (l,mid) (l,mid)和 ( m i d + 1 , r ) (mid+1,r) (mid+1,r)的答案乘起来。
另一种情况是 x x x序列中只有最高位为 0 0 0的或者只有最高位为 1 1 1的数。那么两边的 p p p一定是一一对应的。因为所有的 x i x_i xi最高位是一样的,不用考虑。所以由于一一对应,答案就是 ( l , m i d ) ∗ 2 (l,mid)*2 (l,mid)∗2,因为最高位既可以是 0 0 0又可以是 1 1 1。这种情况的条件是 p ( l , m i d ) = p ( m i d + 1 , r ) p(l,mid)=p(mid+1,r) p(l,mid)=p(mid+1,r),顺次一一相等。
边界就是 l = r l=r l=r时返回 1 1 1。因为相当于 m = 0 m=0 m=0,下面的数只有一个选择。
考试时差点想到…没有发现第二种情况两边的 p p p一一对应。
CODE
#include <cstdio>
const int mod = 1e9 + 7;
const int MAXN = (1<<16)+5;
int n, m, s, p[MAXN]; bool vis[MAXN];
int solve(int l, int r) {
if(l == r) return 1;
int mid = (l + r) >> 1, re = 0;
bool flg = 1;
for(int i = l; i <= mid; ++i)
if(p[i] != p[mid+i-l+1]) { flg = 0; break; }
if(flg) re = (re + 2ll * solve(l, mid) % mod) % mod;
flg = 1;
for(int i = l; i <= mid; ++i) vis[p[i]] = 1;
for(int i = mid+1; i <= r; ++i) if(vis[p[i]]) { flg = 0; break; }
for(int i = l; i <= mid; ++i) vis[p[i]] = 0;
if(flg) re = (re + 1ll * solve(l, mid) * solve(mid+1, r) % mod) % mod;
return re;
}
int main () {
freopen("match.in", "r", stdin);
freopen("match.out", "w", stdout);
scanf("%d%d", &m, &n);
s = 1<<m;
for(int i = 1; i <= s; ++i) scanf("%d", &p[i]);
printf("%d\n", solve(1, s));
}
T3
题意
一棵树,初始时所有边都是黑色。有两种操作:
- 0 0 0:求 a a a到 b b b路径上的黑边数量。
- 1 1 1:将 a a a到 b b b路径上所有边设为白边,然后把只有一个点在这条路径上的边设为黑边。
题解
以为写了
80
80
80分暴力,发现有
30
30
30的数据不符合题目描述(可能是题目描述不符合数据? ),然后就
50
50
50了…
正解比较巧妙,我们把 1 1 1操作看做把 a → b a\to b a→b所有点染上一个新的颜色,一条边为白边当且仅当两个点颜色相同。然后我们树剖后只需要维护线段上不同颜色段数就行了。
CODE
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 200005;
const int MAXQ = 300005;
int n, fir[MAXN], to[MAXN<<1], nxt[MAXN<<1], cnt;
inline void link(int u, int v) {
to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt;
to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt;
}
int fa[MAXN], dep[MAXN], son[MAXN], top[MAXN], dfn[MAXN], sz[MAXN];
int tmr;
void dfs1(int u, int ff) {
dep[u] = dep[fa[u]=ff] + (sz[u]=1);
for(int i = fir[u], v; i; i = nxt[i])
if((v=to[i]) != ff) {
dfs1(v, u), sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp;
dfn[u] = ++tmr;
if(son[u]) dfs2(son[u], tp);
for(int i = fir[u], v; i; i = nxt[i])
if((v=to[i]) != fa[u] && v != son[u])
dfs2(v, v);
}
struct seg {
int l, r, ans;
seg(){}
seg(int l, int r, int ans):l(l), r(r), ans(ans){}
inline seg operator !() const{
return seg(r, l, ans);
}
inline seg operator +(const seg &o)const {
if(!ans) return o;
if(!o.ans) return *this;
return (r^o.l) ? seg(l, o.r, ans + o.ans) : seg(l, o.r, ans + o.ans - 1);
}
}v[MAXN<<2];
int lz[MAXN<<2], now;
inline void upd(int i) { v[i] = v[i<<1] + v[i<<1|1]; }
inline void pd(int i) {
if(~lz[i]) {
lz[i<<1] = lz[i<<1|1] = lz[i];
v[i<<1] = v[i<<1|1] = seg(lz[i], lz[i], 1);
lz[i] = -1;
}
}
void build(int i, int l, int r) {
lz[i] = -1;
if(l == r) { v[i] = seg(l, l, 1); return; }
int mid = (l + r) >> 1;
build(i<<1, l, mid);
build(i<<1|1, mid+1, r);
upd(i);
}
void modify(int i, int l, int r, int x, int y, int z) {
if(x <= l && r <= y) { v[i] = seg(z, z, 1); lz[i] = z; return; }
pd(i);
int mid = (l + r) >> 1;
if(x <= mid) modify(i<<1, l, mid, x, y, z);
if(y > mid) modify(i<<1|1, mid+1, r, x, y, z);
upd(i);
}
seg query(int i, int l, int r, int x, int y) {
if(x <= l && r <= y) { return v[i]; }
pd(i);
int mid = (l + r) >> 1; seg re = seg(-1, -1, 0);
if(x <= mid) re = re + query(i<<1, l, mid, x, y);
if(y > mid) re = re + query(i<<1|1, mid+1, r, x, y);
upd(i);
return re;
}
inline void cover(int a, int b) {
++now;
while(top[a]^top[b]) {
if(dep[top[a]] > dep[top[b]]) modify(1, 1, n, dfn[top[a]], dfn[a], now), a = fa[top[a]];
else modify(1, 1, n, dfn[top[b]], dfn[b], now), b = fa[top[b]];
}
if(dep[a] > dep[b]) modify(1, 1, n, dfn[b], dfn[a], now);
else modify(1, 1, n, dfn[a], dfn[b], now);
}
inline int getans(int a, int b) {
seg L = seg(-1, -1, 0), R = seg(-1, -1, 0);
while(top[a]^top[b]) {
if(dep[top[a]] > dep[top[b]]) L = L + !query(1, 1, n, dfn[top[a]], dfn[a]), a = fa[top[a]];
else R = query(1, 1, n, dfn[top[b]], dfn[b]) + R, b = fa[top[b]];
}
if(dep[a] > dep[b]) L = L + !query(1, 1, n, dfn[b], dfn[a]) + R;
else L = L + query(1, 1, n, dfn[a], dfn[b]) + R;
return L.ans-1;
}
int main () {
freopen("colour.in", "r", stdin);
freopen("colour.out", "w", stdout);
scanf("%d", &n); now = n;
for(int i = 1, x, y; i < n; ++i)
scanf("%d%d", &x, &y), link(x, y);
dfs1(1, 0), dfs2(1, 1);
build(1, 1, n);
int q, op, a, b;
scanf("%d", &q);
while(q-->0) {
scanf("%d%d%d", &op, &a, &b);
if(op == 0) printf("%d\n", getans(a, b));
else cover(a, b);
}
}