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均为质数。
多组数据,组数
T
≤
10
,
n
≤
1
0
5
T \leq 10,n \leq10^5
T≤10,n≤105
分析
发线十万以内的质数只有约10000个,所以我们可以对每一个数提前预处理出将它拆成两个质数相加的方案数。然后用桶的思想做一遍即可。
这个dalao的方法可以做到
T
<
=
1
0
5
T<=10^5
T<=105
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mn = 100005;
bool vis[mn];
int sum, a[mn];
ll f[mn];
int main()
{
freopen("plus.in", "r", stdin);
freopen("plus.out","w",stdout);
int T, n;
for(int i = 2; i <= mn - 5; i++)
{
if(!vis[i])
a[++sum] = i;
for(int j = 1; j <= sum && a[j] * i <= mn - 5; j++)
{
vis[a[j] * i] = 1;
if(i % a[j] == 0)
break;
}
}
for(int i = 1; i <= sum && a[i] <= mn - 5; i++)
for(int j = 1; a[j] + a[i] <= mn - 5 && j <= sum; j++)
++f[a[j]+a[i]];
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
ll ans = 0;
for(int i = 1; i <= n; i++)
ans += f[i] * f[n - i];
printf("%lld\n", ans);
}
}
T2
题意
给定一个
2
m
2^m
2m的序列
{
p
i
}
\{p_i\}
{pi},其中
i
∈
[
0
,
2
m
−
1
]
i \in [0, 2^m-1]
i∈[0,2m−1],计算长为
n
n
n的数列
x
i
{x_i}
xi的数量,满足
x
i
x_i
xi互不相同且对于
i
∈
[
0
,
2
m
−
1
]
,
x
p
i
i \in [0,2^m-1],x_{p_i}
i∈[0,2m−1],xpi^
i
\small i
i为最大值。
m
≤
16
,
1
≤
n
≤
2
m
m \leq 16,1\leq n \leq 2^m
m≤16,1≤n≤2m
分析
若按
p
i
p_i
pi下标的最高位分类,每一个
p
i
p_i
pi都可以被分到两个不同的集合内,且这两个集合无交集。
定义
p
i
,
p
j
p_i,p_j
pi,pj对应
⇔
i
,
j
\Leftrightarrow i,j
⇔i,j最高位不同且剩余位相同。
当
{
x
i
}
\{x_i\}
{xi}的最高位有1和0时,对于对应的
p
i
,
p
j
p_i,p_j
pi,pj,一定有
p
i
≠
p
j
p_i \neq p_j
pi̸=pj(
i
,
j
i,j
i,j最高位不同)。于是我们可以将原问题拆成两个子问题。
当
{
x
i
}
\{x_i\}
{xi}的最高位为0或1时,对于对应的
p
i
,
p
j
p_i,p_j
pi,pj,一定有
p
i
=
p
j
p_i=p_j
pi=pj(
i
,
j
i,j
i,j除最高位外均相同)。也就是说,此时
{
p
i
}
\{p_i\}
{pi}由两个相同序列拼接而成。
综上,若把
p
i
p_i
pi拆成等长的两段,一定满足:要么这两段处处相等,要么处处不等。
根据上面的分析,我们定义函数
F
(
l
,
r
)
F(l,r)
F(l,r)为
p
l
⋯
p
r
p_l \cdots p_r
pl⋯pr的答案,
m
i
d
mid
mid为中点。则:
当任意
i
i
i满足
p
l
+
i
=
p
m
i
d
+
i
p_{l+i}=p_{mid+i}
pl+i=pmid+i时,
F
(
l
,
r
)
=
F
(
l
,
m
i
d
)
∗
F
(
m
i
d
+
1
,
r
)
F(l,r)=F(l,mid)*F(mid+1,r)
F(l,r)=F(l,mid)∗F(mid+1,r)
当任意
i
i
i满足
p
l
+
i
≠
p
m
i
d
+
i
p_{l+i} \neq p_{mid+i}
pl+i̸=pmid+i时,
F
(
l
,
r
)
=
2
F
(
l
,
m
i
d
)
F(l,r)=2F(l,mid)
F(l,r)=2F(l,mid)
否则
F
(
l
,
r
)
=
0
F(l,r)=0
F(l,r)=0
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mn = 66000, mod = 1e9 + 7;
int p[mn];
ll f(int l, int r)
{
if(l == r) return 1;
int mid = (l + r) >> 1, tag = -2;
for(int i = l; i <= mid && tag != -1; i++)
if(p[i] == p[mid + i - l + 1])
tag = (tag == 1) ? -1 : 0;
else
tag = (tag == 0) ? -1 : 1;
if(tag == 1) return f(l, mid) * f(mid + 1, r) % mod;
else if(tag == 0) return 2ll * f(l, mid) % mod;
else return 0;
}
int main()
{
freopen("match.in", "r", stdin);
freopen("match.out","w",stdout);
int m, n;
scanf("%d%d", &m, &n);
for(int i = 1; i <= (1 << m); i++)
scanf("%d", &p[i]);
printf("%lld\n", f(1, (1 << m)));
}
T3
题意
给定一棵树,初始时每一条边为黑色。支持两种操作:查询一条简单路径的黑边条数;修改一条简单路径的边染为白色,再将恰有一个端点为路径上点的边染为黑色。
n
≤
2
∗
1
0
5
,
q
≤
3
∗
1
0
5
n \leq 2*10^5,q \leq3*10^5
n≤2∗105,q≤3∗105
分析
观察发现,一条边为黑色
⇔
\Leftrightarrow
⇔两个端点被不同的询问访问。
也就是说,对于一次修改,我们只需要将路径节点标上询问编号,查询时就是求一条路径上的颜色段数。
树链剖分+线段树维护即可。
#include<bits/stdc++.h>
#define lch (i << 1)
#define rch ((i << 1) | 1)
#define mid ((t[i].l + t[i].r) >> 1)
using namespace std;
const int mn = 200005;
struct seg{
int l, r, lc, rc, tag, sum;
}t[mn << 3];
vector<int> g[mn];
int dep[mn], fa[mn], siz[mn], son[mn], top[mn], num[mn], times;
inline int getint()
{
int ret = 0, flg = 1; char c;
while((c = getchar()) < '0' || c > '9')
if(flg == '-') flg = -1;
while(c >= '0' && c <= '9')
ret = ret * 10 + c - '0', c = getchar();
return ret * flg;
}
void dfs1(int s, int f, int d)
{
fa[s] = f, dep[s] = d, siz[s] = 1;
int m = g[s].size();
for(int i = 0; i < m; i++)
{
int t = g[s][i];
if(t != f)
{
dfs1(t, s, d + 1), siz[s] += siz[t];
if(siz[son[s]] < siz[t])
son[s] = t;
}
}
}
void dfs2(int s)
{
num[s] = ++times;
if(son[s]) top[son[s]] = top[s], dfs2(son[s]);
int m = g[s].size();
for(int i = 0; i < m; i++)
{
int t = g[s][i];
if(t != fa[s] && t != son[s])
top[t] = t, dfs2(t);
}
}
inline int lca(int a, int b)
{
while(top[a] != top[b])
if(dep[top[a]] > dep[top[b]])
a = fa[top[a]];
else
b = fa[top[b]];
return dep[a] < dep[b] ? a : b;
}
void make_tree(int i, int l, int r)
{
t[i] = (seg) {l, r, 0, 0, 0, 0};
if(l == r) {t[i].sum = 1, t[i].lc = t[i].rc = -l; return;}
make_tree(lch, l, mid), make_tree(rch, mid + 1, r);
t[i].lc = t[lch].lc, t[i].rc = t[rch].rc, t[i].sum = t[lch].sum + t[rch].sum;
}
inline void push_down(int i)
{
if(t[i].tag)
t[lch].tag = t[rch].tag = t[i].lc = t[i].rc = t[i].tag, t[i].sum = 1, t[i].tag = 0;
}
void edit_tree(int i, int l, int r, int v)
{
push_down(i);
if(t[i].l > r || t[i].r < l) return;
if(t[i].l == l && t[i].r == r)
{
t[i].tag = v, push_down(i);
return;
}
if(r <= mid || l > mid)
edit_tree(lch, l, r, v), edit_tree(rch, l, r, v);
else
edit_tree(lch, l, mid, v), edit_tree(rch, mid + 1, r, v);
t[i].sum = t[lch].sum + t[rch].sum - (t[lch].rc == t[rch].lc);
t[i].lc = t[lch].lc, t[i].rc = t[rch].rc;
}
int getsum(int i, int l, int r)
{
push_down(i);
if(t[i].l > r || t[i].r < l) return 0;
if(t[i].l == l && t[i].r == r)
return t[i].sum;
int ret;
if(r <= mid || l > mid)
ret = getsum(lch, l, r) + getsum(rch, l, r);
else
ret = getsum(lch, l, mid) + getsum(rch, mid + 1, r) - (t[lch].rc == t[rch].lc);
t[i].sum = t[lch].sum + t[rch].sum - (t[lch].rc == t[rch].lc), t[i].lc = t[lch].lc, t[i].rc = t[rch].rc;;
return ret;
}
int getclr(int i, int p)
{
push_down(i);
if(t[i].l == p && t[i].r == p) return t[i].lc;
if(p <= mid) return getclr(lch, p);
else return getclr(rch, p);
}
inline void edit(int a, int to, int v)
{
while(top[a] != top[to])
edit_tree(1, num[top[a]], num[a], v), a = fa[top[a]];
edit_tree(1, num[to], num[a], v);
}
inline int sum(int a, int to)
{
int ret = 0, pre = -1;
while(top[a] != top[to])
ret += getsum(1, num[top[a]], num[a]) - (pre != -1 && getclr(1, pre) == getclr(1, num[a])), pre = num[top[a]], a = fa[top[a]];
return ret + getsum(1, num[to], num[a]) - (pre != -1 && getclr(1, pre) == getclr(1, num[a]));
}
int main()
{
freopen("colour.in", "r", stdin);
freopen("colour.out","w",stdout);
int n, m, T, a, b, c;
n = getint();
for(int i = 1; i < n; i++)
a = getint(), b = getint(), g[a].push_back(b), g[b].push_back(a);
dfs1(1, 0, 1), top[1] = 1, dfs2(1), make_tree(1, 1, n), m = getint();
for(int i = 1; i <= m; i++)
{
T = getint(), a = getint(), b = getint(), c = lca(a, b);
if(T) edit(a, c, i), edit(b, c, i);
else printf("%d\n", sum(a, c) + sum(b, c) - 2);
}
}