【20190907】七校联考

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 T10,n105
分析
发线十万以内的质数只有约10000个,所以我们可以对每一个数提前预处理出将它拆成两个质数相加的方案数。然后用桶的思想做一遍即可。
这个dalao的方法可以做到 T &lt; = 1 0 5 T&lt;=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,2m1],计算长为 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,2m1],xpi^ i \small i i为最大值。
m ≤ 16 , 1 ≤ n ≤ 2 m m \leq 16,1\leq n \leq 2^m m16,1n2m
分析
若按 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 plpr的答案, 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 n2105,q3105
分析
观察发现,一条边为黑色 ⇔ \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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值