杭电多校第六场8月5日补题记录

A Yes, Prime Minister

题意:给定一个 x x x,问包含 x x x 的最小连续区间 [ l , r ] [l,r] [l,r] 满足 ∑ i = l r i \displaystyle \sum_{i=l}^{r}i i=lri 为质数的最小区间长度 r − l + 1 r-l+1 rl+1

解法:首先一定有解。由于此处要求质数为一大于 2 2 2 的数,因而考虑以下几种情况:

  1. x x x 为质数。区间长度为 1 1 1
  2. 2 x − 1 2x-1 2x1 为质数。区间为 [ x − 1 , x ] [x-1,x] [x1,x],长度为 2 2 2
  3. 2 x + 1 2x+1 2x+1 为质数。区间为 [ x , x + 1 ] [x,x+1] [x,x+1],长度为 2 2 2
  4. 找到一个 y > ∣ x ∣ y>|x| y>x 满足上述三个条件之一的,取区间 [ − y + 1 , y ] [-y+1,y] [y+1,y]

如果区间中每个数为正数,那么长度一定不超过 2 2 2——考虑和为 ( l + r ) ( r − l + 1 ) 2 \displaystyle \frac{(l+r)(r-l+1)}{2} 2(l+r)(rl+1),如果超过 3 3 3 则一定为合数。对于其他的情况,需要考虑负数可以消除下面的数,只暴露所需要的一段。

因而做法就是:线性筛出 2 × 1 0 7 2\times 10^7 2×107 以内的质数,然后对每一个数都求出距离最近的满足要求的数(可以使用递推),具体细节参看代码。整体复杂度 O ( n ) O(n) O(n)

#include <cstdio>
#include <algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 10000000;
int L1[2 * N + 50], L2[2 * N + 50];
int prime[2 * N + 50], tot;
bool vis[2 * N + 50];
void sieve(int n)
{
    vis[0] = vis[1] = 1;
    for (int i = 2; i <= n;i++)
    {
        if(!vis[i])
            prime[++tot] = i;
        for (int j = 1; j <= tot && i * prime[j] <= n; j++)
        {
            vis[i * prime[j]] = 1;
            if(i%prime[j]==0)
                break;
        }
    }
    for (int i = 1; i <= n; i++)
        if(!vis[i])
            L1[i] = i;//本身就是质数的
    for (int i = n; i >= 1;i--)
        if(!L1[i])
            L1[i] = L1[i + 1];//最近的质数。这里是为了找y
    for (int i = 1; i <= n / 2; i++)
        if (!vis[2 * i + 1])
            L2[i] = i;//2x-1型。这里也是为了找y
    for (int i = n / 2; i >= 1; i--)
        if(!L2[i])
            L2[i] = L2[i + 1];
}
int main()
{
    sieve(2 * N + 5);
    int n, t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        if(n==0)
        {
            printf("3\n");
            continue;
        }
        int ans = inf;
        if(n<0)//负数不能直接找,只能用第四条
            ans = min(2 * L1[1 - n], 2 * L2[1 - n] + 1);
        else
        {
            ans = min(2 * L1[n], 2 * L2[n] + 1);
            if(!vis[n])//正数可以用前三种方法
                ans = min(ans, 1);
            if (!vis[2 * n - 1] || !vis[2 * n + 1])
                ans = min(ans, 2);
        }
        printf("%d\n", ans);
    }
    return 0;
}

C 0 tree

题意:给定一棵 n n n 个节点的树,每个点有点权 a i a_i ai,边有边权 c i c_i ci。可以执行不超过 4 n 4n 4n 次这样的操作:

选定树上的一条链 ( u , v ) (u,v) (u,v) 和一非负数 w w w a u ← a u ⊕ w a_u \leftarrow a_u \oplus w auauw a v ← a v ⊕ w a_v \leftarrow a_v \oplus w avavw,记链 ( u , v ) (u,v) (u,v) x 0 , x 1 , x 2 , ⋯   , x k x_0,x_1,x_2,\cdots,x_k x0,x1,x2,,xk,链上的边分别为 e 0 , e 1 , ⋯   , e k − 1 e_0,e_1,\cdots,e_{k-1} e0,e1,,ek1,则 c i ← c i + ( − 1 ) i w c_i \leftarrow c_{i}+(-1)^{i}w cici+(1)iw。问有无合法的操作序列能满足要求。

解法:观察变化的性质。如果我们将边权转移到点上,令点权 b i = ∑ e j = ⟨ i , v ⟩ c e j \displaystyle b_i=\sum_{e_j=\langle i,v\rangle} c_{e_j} bi=ej=i,vcej,即和 i i i 相连的所有边的边权和,则此时的操作只会对端点有关——因为中间路径上的每一条点周围的边权和都会先被加一次 w w w 又再减掉,因而边权和不变。

由于对于 a a a 的操作都是异或,对于 b b b 的操作都是加减,因而不妨令 a a a 为异或点权, b b b 为求和点权。现在我们已经将问题和路径完全剥离开了,考虑到树是一个二分图,因而可以将树剖成两个独立的点集去处理。每一个点集满足其内部全部点的距离都是偶数。

首先考虑其内部如何消除,或者将点集内的其余点的两种点权都集中到一个点上。考虑一个点集,不妨将全部点权都集中到 1 1 1 点上。对于点 i i i,一个可行的操作序列是:

  1. w w w a i a_i ai
  2. w w w − a i + b i 2 \displaystyle -\frac{a_i+b_i}{2} 2ai+bi。这里的 b i b_i bi 仍然是原来的 b i b_i bi
  3. 再取一次 w = − a i + b i 2 w=\displaystyle -\frac{a_i+b_i}{2} w=2ai+bi

第一步直接消去了 a i a_i ai。第二步和第三步之所以要消除两遍是因为不能影响已经变成 0 0 0 a i a_i ai。经过这个操作之后, a i a_i ai b i b_i bi 都能归零。但是进行这些操作有几个前提:

  1. a i a_i ai b i b_i bi 奇偶性相同。如果奇偶性不同,则 b i ⊕ a i b_i \oplus a_i biai 不为偶数,无法除以 2 2 2
  2. b i + a i ≤ 0 b_i+a_i\leq 0 bi+ai0。否则 − a i + b i 2 < 0 \displaystyle -\frac{a_i + b_i}{2}<0 2ai+bi<0

经过这样的 O ( 3 n ) O(3n) O(3n) 次操作,点权就都到两个点集 A , B A,B A,B 中的一个点了。此时,两个点点权分别为 ⨁ i ∈ A a i , ⨁ i ∈ B a i \displaystyle \bigoplus_{i \in A} a_i,\bigoplus_{i \in B} a_i iAai,iBai ∑ i ∈ A b i , ∑ i ∈ B b i \displaystyle \sum_{i \in A}b_i, \sum_{i \in B}b_i iAbi,iBbi。如果 ⨁ i ∈ A a i ≠ ⨁ i ∈ B a i \displaystyle \bigoplus_{i \in A} a_i \neq\bigoplus_{i \in B} a_i iAai=iBai 那么无解。对于剩下的求和点权可以用类似的方式处理。

当然可以先处理总点权,让两个点集内部总的两种点权和都为 0 0 0 然后再处理内部,代码中即是这种实现。

整体复杂度 O ( n ) O(n) O(n)

#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
struct op
{
    int u;
    int v;
    long long w;
};
vector<op> ans;
bool flag = 0;
vector<long long> a, b;
vector<int> depth;
bool cmp(int x,int y)
{
    return b[x] < b[y];
}
void operation(int u,int v,long long w)
{
    ans.push_back((op){u, v, w});
    a[u] ^= w;
    b[u] += w;
    a[v] ^= w;
    if((depth[u]&1)!=(depth[v]&1))
        b[v] += w;
    else
        b[v] -= w;
}
void inner(vector<int> &seq)//点集内部的处理
{
    int n = seq.size();
    if(n==1)
        return;
    for (int i = 0; i + 1 < n; i++)
        operation(seq[i], seq[i + 1], a[seq[i]]);
    sort(seq.begin(), seq.end(), cmp);
    int left = 0, right = n - 1;
    while (left < right)
    {
        long long now = min(b[seq[right]], -b[seq[left]]);
        if(now%2)
        {
            flag = 1;
            return;
        }
        operation(seq[left], seq[right], now / 2);
        operation(seq[left], seq[right], now / 2);
        if (left < right && b[seq[left]]==0)
            left++;
        if (left < right && b[seq[right]] == 0)
            right--;
    }
    return;
}
int main()
{
    int n, t;
    int u, v;
    long long w;
    scanf("%d", &t);
    while(t--)
    {
        ans.clear();
        flag = 0;
        scanf("%d", &n);
        vector<int> odd, even;
        vector<vector<int>> edge(n + 1, vector<int>(0, 0));
        a.clear();
        b.clear();
        depth.clear();
        a.resize(n + 1);
        b.resize(n + 1);
        depth.resize(n + 1);
        for (int i = 1; i <= n;i++)
            scanf("%lld", &a[i]);
        for (int i = 1; i < n; i++)
        {
            scanf("%d%d%lld", &u, &v, &w);
            edge[u].push_back(v);
            edge[v].push_back(u);
            b[u] += w;
            b[v] += w;
        }
        if(n==1)
        {
            if(a[1]==0)
                printf("YES\n0\n");
            else
                printf("NO\n");
            continue;
        }
        queue<int> q;
        q.push(1);
        depth[1] = 1;
        while(!q.empty())
        {
            int tp = q.front();
            q.pop();
            for(auto i:edge[tp])
                if(!depth[i])
                {
                    depth[i] = depth[tp] + 1;
                    q.push(i);
                }
        }
        long long sum[2] = {0}, xorsum[2] = {0};
        for (int i = 1; i <= n;i++)
            if(depth[i]&1)//根据深度染色
            {
                odd.push_back(i);
                xorsum[1] ^= a[i];
                sum[1] += b[i];
                if((a[i]&1)!=(b[i]&1))
                {
                    flag = 1;
                    break;
                }
            }
            else
            {
                xorsum[0] ^= a[i];
                sum[0] += b[i];
                even.push_back(i);
                if((a[i]&1)!=(b[i]&1))
                {
                    flag = 1;
                    break;
                }
            }
        if(xorsum[0]!=xorsum[1] || xorsum[0]>-sum[0])
            flag = 1;
        if (flag)
        {
            printf("NO\n");
            continue;
        }
        operation(odd[0], even[0], xorsum[0]);
        operation(odd[0], even[0], -(sum[0] + xorsum[0]) / 2);
        operation(odd[0], even[0], -(sum[0] + xorsum[0]) / 2);
        inner(odd);
        inner(even);
        printf("YES\n");
        printf("%d\n", ans.size());
        for(auto i:ans)
            printf("%d %d %lld\n", i.u, i.v, i.w);
    }
    return 0;
}

D Decomposition

题意:给定一个 n n n 个节点的完全图,找到路径总长度为 n ( n − 1 ) 2 \displaystyle \frac{n(n-1)}{2} 2n(n1) k k k 条简单路径,使得第 i i i 条路径的长度为给定的 l i l_i li。满足 n n n 为奇数且 l i ≤ n − 3 l_i \leq n-3 lin3

解法:用如下的方式可以找到 ⌊ n 2 ⌋ \displaystyle \lfloor \frac{n}{2} \rfloor 2n 条长度为 n − 1 n-1 n1 的回路:

在这里插入图片描述

其构造的原理为:找到一个中心起始点,例如 n n n,然后每次连接 ( i , − i ) m o d    n − 1 (i,-i) \mod n-1 (i,i)modn1,然后连接 1 1 1 ⌊ n 2 ⌋ \displaystyle \lfloor \frac{n}{2} \rfloor 2n。这样构成一条回路。每次旋转 2 π 2 n − 1 \displaystyle 2 \pi \frac{2}{n-1} 2πn12 即可依次找到剩余几个回路(完整代码中是这种方法)。可以证明,这 ⌊ n 2 ⌋ \displaystyle \lfloor \frac{n}{2} \rfloor 2n 条回路全部连接起来就是一条完整的欧拉回路。

可以用以下的代码生成:

	--n,G.clear();
    fp(i,1,n>>1){
        G.push_back(1);
        for(int u=i-1,j=1;j<=n;++j)
            G.push_back(u+2),u=j&1?(u-j+n)%n:(u+j)%n;
    }

可以证明,这样连接起来的欧拉回路,相同的两点之间间距至少为 n − 1 n-1 n1,满足题目 n − 3 n-3 n3 的要求。这是因为中心点之间相距为固定的 n n n,而每次每一个点出现的位置至多只会向前移动 1 1 1 位。因而只需要把这样的欧拉回路拉出来每次截取其中的 l i l_i li 长度部分即可。

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
    int t, n, k, times = 0;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &k);
        vector<int> ans, route;
        route.push_back(n);
        route.push_back(1);
        for (int i = 2; i <= n / 2; i++)
        {
            route.push_back(i);
            route.push_back(n + 1 - i);
        }
        route.push_back((n + 1) / 2);
        for (int i = 0; i < n / 2;i++)//旋转得到剩余部分。注意 n 要特判
        {
            ans.push_back(n);
            for (int j = 1; j < route.size();j++)
                ans.push_back((route[j] + i - 1) % (n - 1) + 1);
        }
        ans.push_back(n);//记得最后插入终点
        int place = 0;
        printf("Case #%d:\n", ++times);
        for (int i = 1; i <= k; i++)
        {
            int a;
            scanf("%d", &a);
            while(a--)
            {
                printf("%d ", ans[place]);    
                place++;
            }
            printf("%d\n", ans[place]);
        }
    }
    return 0;
}

E Median

题意: 1 1 1 n n n 的序列要求分割成 m m m 段,每一段的中位数为给定的 b i b_i bi。此处中位数定义与正常定义不同,偶数个时为中间偏左的数。给定 m m m 个中位数 b i b_i bi,问能否有一种合适的划分。

解法:由于没有平均,因而这 m m m 个中位数应当被独立的分到这些区间中。考虑两个中位数之间构成的序列。中间的这些数可以通过两侧的中位数与外面的数(非中位数、非此区间内的数)进行对应匹配。如果能构成一一对应关系,或者多出来的数能和前面小的中位数匹配(每一个小的中位数后面可以跟一个大的数,依旧可以保持中位数不变),那么就可以成立;反之不成立。因而只需要枚举这些区间即可,无需只关注最大——每一个都满足等价于最大的满足。

#include <cstdio>
#include <algorithm>
using namespace std;
int b[100005];
int main()
{
    int t, n, m;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m;i++)
            scanf("%d", &b[i]);
        sort(b + 1, b + m + 1);
        b[m + 1] = n + 1;
        bool flag = 0;
        for (int i = 1; i <= m + 1;i++)
            if (2 * (b[i] - b[i - 1] - 1) > i - 1 + n - m)
            //2*(b[i]-b[i-1]-1) 为完全匹配所需要的数(含自己)。
            //i-1是左侧中位数个数。n-m为全域待分配的数
            {
                flag = 1;
                break;
            }
        if(flag)
            printf("NO\n");
        else
            printf("YES\n");
    }
    return 0;
}

G Power Station of Art

题意:给定两张点和连边关系都相同的图,只有点的点权和颜色(仅红黑)不同。现在可以执行若干次操作——交换两点的点权,如果颜色相同则翻转反之不变。问是否能将其中一张图变成另一张。

解法:考虑交换问题的实质——数字一定要交换;颜色可以认为是交换之后再翻转。因而都要进行交换操作。这一操作具有可逆性,因而只需要考虑如何将老图变成新图即可。

一个最基本的判断——如果数字集合不同,那么一定无解:无论怎么交换,数字集合不会发生变化,因而首先将数字全部归还到位。下面的讨论基于这一点展开。

显然,一个点如果被交换了奇数次,则颜色要翻转;偶数次则不翻转。这种奇偶性问题通常应该放在二分图去考虑——如果在染色后的二分图上执行交换翻转操作,由于只存在偶数大小的环,点上颜色和二分图染的颜色一定是匹配的。

如果图是一个二分图:那么原先的颜色和二分图上的颜色一定是挂钩的,不可更改。因而将原来的数集按照[原色^二分图]上颜色进行划分,新老图上必须完全一样。

如果原图不是二分图:可以认为这一张图是由某一个二分图加上了若干条边构成的,且必然存在奇数大小的环。首先先将数字归还到位,颜色错误的先放在一边。此时由于有奇数大小的环,可以使用以下的三步操作实现数字不变,仅有两个点颜色翻转。记奇环上点为 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n

  1. 1 1 1 号点的点权通过 n − 1 n-1 n1 次相邻交换 ( i , i + 1 ) (i,i+1) (i,i+1) 交换到 n n n。此时其余节点交换 1 1 1 次, 1 1 1 号节点交换 n − 1 n-1 n1 次。此时原 n n n 号节点的权值在 n − 1 n-1 n1 处, 2 2 2 号节点的权值在 n n n 处,其余点 i i i 的点权都是在 i − 1 i-1 i1
  2. 交换现在的 1 1 1 号节点与 n n n 号节点。现在 1 1 1 经过了 n n n 次操作物归原主,同时经过奇数次操作后颜色翻转。 2 2 2 号点的权值转移到了 n − 1 n-1 n1
  3. 将原 2 2 2 号点的权值经过 n − 2 n-2 n2 次操作 ( i , i − 1 ) (i,i-1) (i,i1) 还回去。此时其余节点又多交换了一次,并且值也回来了,颜色没有动; 2 2 2 号节点的权值经历了 n − 2 n-2 n2 次操作,颜色翻转,又回到了 2 2 2

因而基于这一系列操作,任意两个点颜色的错误都可以转移到某一个奇环上得到修正(包括不在奇环上的,可以转移过去),但是要满足奇偶性——因为只能两个两个的修改。所以这里需要满足的只是数集相同与颜色数目奇偶性相同。

#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
using namespace std;
struct line
{
    int from;
    int to;
    int next;
};
struct line que[2000005];
int cnt, headers[1000005];
void add(int from,int to)
{
    cnt++;
    que[cnt].from = from;
    que[cnt].to = to;
    que[cnt].next = headers[from];
    headers[from] = cnt;
}
int num[2][1000005], col[1000005];
char color[2][1000005];
bool check(int x)
{
    vector<int> circle;
    queue<int> q;
    q.push(x);
    circle.push_back(x);
    bool flag = 1;
    col[x] = 1;
    while(!q.empty())
    {
        int tp = q.front();
        q.pop();
        for (int i = headers[tp]; i;i=que[i].next)
            if(col[que[i].to]==-1)
            {
                col[que[i].to] = col[tp] ^ 1;
                circle.push_back(que[i].to);
                q.push(que[i].to);
            }
            else
                if(col[que[i].to]==col[tp])
                    flag = 0;
    }
    if(flag)
    {
        multiset<int> former[2], latter[2];
        for(auto i:circle)
        {
            if(color[0][i]=='R')
                color[0][i] = 1;
            else
                color[0][i] = 0;
            if(color[1][i]=='R')
                color[1][i] = 1;
            else
                color[1][i] = 0;
        }
        for(auto i:circle)
        {
            former[color[0][i] ^ col[i]].insert(num[0][i]);//新老颜色共同决定所属数集
            latter[color[1][i] ^ col[i]].insert(num[1][i]);
        }
        return former[0] == latter[0] && former[1] == latter[1];
    }
    else
    {
        int lattercnt = 0, formercnt = 0;
        multiset<int> former, latter;
        for(auto i:circle)
        {
            former.insert(num[0][i]);
            latter.insert(num[1][i]);
            if(color[0][i]=='R')
                formercnt ^= 1;
            if(color[1][i]=='R')
                lattercnt ^= 1;
        }
        return former == latter && formercnt == lattercnt;
    }
}
int main()
{
    int t, n, m, u, v;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n;i++)
        {
            headers[i] = 0;
            col[i] = -1;
        }
        cnt = 0;
        for (int i = 1; i <= m; i++)
        {
            scanf("%d%d", &u, &v);
            add(u, v);
            add(v, u);
        }
        for (int i = 1; i <= n;i++)
            scanf("%d", &num[0][i]);
        scanf("%s", color[0] + 1);
        for (int i = 1; i <= n;i++)
            scanf("%d", &num[1][i]);
        scanf("%s", color[1] + 1);
        bool ans = 1;
        for (int i = 1; i <= n;i++)
            if(col[i]==-1)
                ans &= check(i);
        if(ans)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

J Array

题意:给定一个长度为 n n n 的序列 B B B,需要找到这样的 A A A 是否存在: ∀ i ∈ [ 1 , n ] \forall i \in [1,n] i[1,n] [ i , B i ] [i,B_i] [i,Bi] 中要出现 [ 1 , n ] [1,n] [1,n] 中全部元素,而 [ i , B i − 1 ] [i,B_i-1] [i,Bi1] 不能满足。若 B i = n + 1 B_i=n+1 Bi=n+1,则 [ i , n ] [i,n] [i,n] 中元素只是 A A A 中元素的真子集。满足 B i B_i Bi 不严格单增。

解法:(挖坑)

K Game

题意:给定 n n n 个特殊区间 [ l i , r i ] [l_i,r_i] [li,ri] 与对应的状态 z i z_i zi,若 z i = 0 z_i=0 zi=0 则表示先手遇到 [ l i , r i ] [l_i,r_i] [li,ri] 局面时直接获胜,反之直接失败。每一次可以将现有区间 [ l , r ] [l,r] [l,r] 转变成 [ l + 1 , r ] [l+1,r] [l+1,r] [ l , r − 1 ] [l,r-1] [l,r1],区间变成空集者输。 q q q 组询问,问给定初始条件下先手必胜还是后手必胜。 n , q ≤ 1 × 1 0 5 n ,q\leq 1\times 10^5 n,q1×105

解法:当现有状态远离任何一个特殊区间时,完全不必考虑太多——直接一个拿左一个拿右即可。基于这样的考量,容易发现 ( i , j ) (i,j) (i,j) ( i + 1 , j − 1 ) (i+1,j-1) (i+1,j1) 胜负情况相同。因而可以按 x + y x+y x+y 对全部情况进行划分。

考虑快临近特殊区间或者快结束的情况——直接暴力搜索即可。因为特殊区间比较少,因而无法直接由 ( i , j ) (i,j) (i,j) 递推到 ( i + 1 , j − 1 ) (i+1,j-1) (i+1,j1)。但是这样点的数目只有 O ( n ) O(n) O(n),因而可以接受这样的暴力记忆化搜索。

时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn),实现的细节参看代码。

#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
const int inf = 0x3f3f3f3f;
map<int, map<int, int>> f;
//f为一个map,存储了一系列的x+y,每一个x+y下都有一个map表示这种局面下有无必胜必败态,含初始的特殊点
//f[x+y] 为一个map,存储了在局面x+y为一定值的情况下 (x,y) 的获胜情况。-1表示未定,0或1表示已经搜索过了
void insert(int x,int y,int z)
{
    if (f.count(x + y) && f[x + y].count(x) && f[x + y][x] != -1)
    //如果之前不是-1,已经搜索出了必胜或必败,那么不要被-1重新恢复成未定
        return;
    f[x + y][x] = z;
}
int query(int x,int y)
{
    if (f.count(x + y) == 0 || f[x + y].count(x) == 0)
        return -1;//当前状态未搜索到确定状态与特殊点。
    if (f[x + y][x] == -1)
        return -2;//离特殊点很近了,需要直接搜索
    else
        return f[x + y][x];
}
int dfs(int x,int y)
{
    int res = query(x, y);
    if(res>=0)//0 或 1,已经搜索过了
        return res;
    if (y - x + 1 <= 2)//如果区间快结束了也是需要直接搜索的局面
        res = -2;
    int ans = 0;
    if(res==-1)//离特殊点还远,那么先直接将左端点或右端点规约到这个特殊局面
    {
        int step = (x + y) / 2, nearest = inf;
        //此处初始化的step表示拿完的局面
        auto it = f[x + y].upper_bound(x);
        if(it!=f[x+y].end())
            nearest = it->second;
        step = min(step, nearest);
        ans = dfs(step, y - (step - x));
    }
    else//离特殊点很近了,直接搜索
    {
        if(x==y)
            return ans = 0;
        else
            ans = !(dfs(x + 1, y) & dfs(x, y - 1));
    }
    insert(x, y, ans);
    return ans;
}
int main()
{
    int t, n, q, x, y, z;
    scanf("%d", &t);
    while(t--)
    {
        f.clear();
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n;i++)
        {
            scanf("%d%d%d", &x, &y, &z);
            insert(x, y, z);
            for (int j = 0; j <= 2;j++)//在特殊点附近的一些,不可以直接规约,应该当作待搜索的未定状态。
                for (int k = 0; k <= 2;k++)
                    if(x-j>=1)
                        insert(x - j, y + k, -1);
        }
        while(q--)
        {
            scanf("%d%d", &x, &y);
            printf("%d", dfs(x, y));
        }
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值