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=l∑ri 为质数的最小区间长度 r − l + 1 r-l+1 r−l+1。
解法:首先一定有解。由于此处要求质数为一大于 2 2 2 的数,因而考虑以下几种情况:
- x x x 为质数。区间长度为 1 1 1。
- 2 x − 1 2x-1 2x−1 为质数。区间为 [ x − 1 , x ] [x-1,x] [x−1,x],长度为 2 2 2。
- 2 x + 1 2x+1 2x+1 为质数。区间为 [ x , x + 1 ] [x,x+1] [x,x+1],长度为 2 2 2。
- 找到一个 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)(r−l+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 au←au⊕w, a v ← a v ⊕ w a_v \leftarrow a_v \oplus w av←av⊕w,记链 ( 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,⋯,ek−1,则 c i ← c i + ( − 1 ) i w c_i \leftarrow c_{i}+(-1)^{i}w ci←ci+(−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,v⟩∑cej,即和 i i i 相连的所有边的边权和,则此时的操作只会对端点有关——因为中间路径上的每一条点周围的边权和都会先被加一次 w w w 又再减掉,因而边权和不变。
由于对于 a a a 的操作都是异或,对于 b b b 的操作都是加减,因而不妨令 a a a 为异或点权, b b b 为求和点权。现在我们已经将问题和路径完全剥离开了,考虑到树是一个二分图,因而可以将树剖成两个独立的点集去处理。每一个点集满足其内部全部点的距离都是偶数。
首先考虑其内部如何消除,或者将点集内的其余点的两种点权都集中到一个点上。考虑一个点集,不妨将全部点权都集中到 1 1 1 点上。对于点 i i i,一个可行的操作序列是:
- 取 w w w 为 a i a_i ai。
- 取 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。
- 再取一次 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 都能归零。但是进行这些操作有几个前提:
- a i a_i ai 与 b i b_i bi 奇偶性相同。如果奇偶性不同,则 b i ⊕ a i b_i \oplus a_i bi⊕ai 不为偶数,无法除以 2 2 2。
- b i + a i ≤ 0 b_i+a_i\leq 0 bi+ai≤0。否则 − 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 i∈A⨁ai,i∈B⨁ai 与 ∑ i ∈ A b i , ∑ i ∈ B b i \displaystyle \sum_{i \in A}b_i, \sum_{i \in B}b_i i∈A∑bi,i∈B∑bi。如果 ⨁ i ∈ A a i ≠ ⨁ i ∈ B a i \displaystyle \bigoplus_{i \in A} a_i \neq\bigoplus_{i \in B} a_i i∈A⨁ai=i∈B⨁ai 那么无解。对于剩下的求和点权可以用类似的方式处理。
当然可以先处理总点权,让两个点集内部总的两种点权和都为 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(n−1) 的 k k k 条简单路径,使得第 i i i 条路径的长度为给定的 l i l_i li。满足 n n n 为奇数且 l i ≤ n − 3 l_i \leq n-3 li≤n−3。
解法:用如下的方式可以找到 ⌊ n 2 ⌋ \displaystyle \lfloor \frac{n}{2} \rfloor ⌊2n⌋ 条长度为 n − 1 n-1 n−1 的回路:
其构造的原理为:找到一个中心起始点,例如 n n n,然后每次连接 ( i , − i ) m o d n − 1 (i,-i) \mod n-1 (i,−i)modn−1,然后连接 1 1 1 与 ⌊ n 2 ⌋ \displaystyle \lfloor \frac{n}{2} \rfloor ⌊2n⌋。这样构成一条回路。每次旋转 2 π 2 n − 1 \displaystyle 2 \pi \frac{2}{n-1} 2πn−12 即可依次找到剩余几个回路(完整代码中是这种方法)。可以证明,这 ⌊ 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 n−1,满足题目 n − 3 n-3 n−3 的要求。这是因为中心点之间相距为固定的 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 号点的点权通过 n − 1 n-1 n−1 次相邻交换 ( i , i + 1 ) (i,i+1) (i,i+1) 交换到 n n n。此时其余节点交换 1 1 1 次, 1 1 1 号节点交换 n − 1 n-1 n−1 次。此时原 n n n 号节点的权值在 n − 1 n-1 n−1 处, 2 2 2 号节点的权值在 n n n 处,其余点 i i i 的点权都是在 i − 1 i-1 i−1。
- 交换现在的 1 1 1 号节点与 n n n 号节点。现在 1 1 1 经过了 n n n 次操作物归原主,同时经过奇数次操作后颜色翻转。 2 2 2 号点的权值转移到了 n − 1 n-1 n−1。
- 将原 2 2 2 号点的权值经过 n − 2 n-2 n−2 次操作 ( i , i − 1 ) (i,i-1) (i,i−1) 还回去。此时其余节点又多交换了一次,并且值也回来了,颜色没有动; 2 2 2 号节点的权值经历了 n − 2 n-2 n−2 次操作,颜色翻转,又回到了 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,Bi−1] 不能满足。若 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,r−1],区间变成空集者输。 q q q 组询问,问给定初始条件下先手必胜还是后手必胜。 n , q ≤ 1 × 1 0 5 n ,q\leq 1\times 10^5 n,q≤1×105
解法:当现有状态远离任何一个特殊区间时,完全不必考虑太多——直接一个拿左一个拿右即可。基于这样的考量,容易发现 ( i , j ) (i,j) (i,j) 与 ( i + 1 , j − 1 ) (i+1,j-1) (i+1,j−1) 胜负情况相同。因而可以按 x + y x+y x+y 对全部情况进行划分。
考虑快临近特殊区间或者快结束的情况——直接暴力搜索即可。因为特殊区间比较少,因而无法直接由 ( i , j ) (i,j) (i,j) 递推到 ( i + 1 , j − 1 ) (i+1,j-1) (i+1,j−1)。但是这样点的数目只有 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;
}