「UOI」 Beginner Contest 002 & Round 4 Tutorial
「UOI」 Beginner Contest 002 & Round 4 Tutorial
A
sol
可以观察到,如果存在一位是
若所有的
答案至多有两个,并且两种答案中
乘以
当然,更简单的方法是,枚举
std
#include <iostream>
using namespace std;
#define MAXN 1000006
int n;
int v[MAXN], r[MAXN];
bool check(int l)
{
int a = l;
for (int i = n - 1; i; i--)
{
a = (v[i] + 10 - (a > 4)) % 10;
}
return (l + (a > 4)) % 10 == v[n];
}
void add(int l)
{
r[n] += l;
for (int i = n - 1; i; i--)
{
r[i] += l = (v[i] + 10 - (l > 4)) % 10;
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> v[i];
r[i] = 0;
}
if (check(v[n]))
{
add(v[n]);
}
if (check((v[n] + 9) % 10))
{
add((v[n] + 9) % 10);
}
for (int i = 1; i <= n; i++)
{
cout << r[i];
}
cout << endl;
return 0;
}
B
sol
挺诈骗的一题啊。
首先询问中我们的目标是把所有数变相等。不难看出实际上是把所有数变为最大值。
考虑变形询问中的操作
此时不难想到,一个数需要变成最大值无非通过反复加一或者与旁边的数组成长为
由于 order_of_key() 函数做个差分找区间某数的出现次数即可。修改的话,在原 tree 中删除下标
std
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
const int N=200010;
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> tr[N];
int mx[N<<2],a[N],mxcnt[4];
void build(int s,int t,int p)
{
if(s==t)
{
mx[p]=a[s];
return;
}
int m=s+t>>1;
build(s,m,p<<1);build(m+1,t,p<<1|1);
mx[p]=max(mx[p<<1],mx[p<<1|1]);
}
void upd(int k,int s,int t,int p,int c)
{
if(s==t)
{
mx[p]=c;
return;
}
int m=s+t>>1;
if(k<=m) upd(k,s,m,p<<1,c);
else upd(k,m+1,t,p<<1|1,c);
mx[p]=max(mx[p<<1],mx[p<<1|1]);
}
int qry(int l,int r,int s,int t,int p)
{
if(l<=s&&t<=r) return mx[p];
int m=s+t>>1,ans=0;
if(l<=m) ans=qry(l,r,s,m,p<<1);
if(r>m) ans=max(ans,qry(l,r,m+1,t,p<<1|1));
return ans;
}
int main()
{
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i],tr[a[i]].insert(i);
build(1,n,1);
while(q--)
{
int opt,x,y;
cin>>opt>>x>>y;
if(opt==1)
{
upd(x,1,n,1,y);
tr[a[x]].erase(x);
a[x]=y;
tr[a[x]].insert(x);
}
else
{
int mx=qry(x,y,1,n,1);
int ans=0;
for(int i=0;i<4;i++)
{
if(mx-i>0&&tr[mx-i].size())
mxcnt[i]=(tr[mx-i].order_of_key(y+1)-tr[mx-i].order_of_key(x));
else
mxcnt[i]=0;
ans+=mxcnt[i]*i;
}
cout<<ans+(y-x+1-mxcnt[0]-mxcnt[1]-mxcnt[2]-mxcnt[3])*4<<'\n';
}
}
}
C
sol by cosf
下面用
首先我们先转化题意:
给定一个
n 个点的树,每次询问给定区间[l, r] ,求是否存在一个一个异于[l, r] 且在[l, r] 最小连通块中的点p 。还有一个特例:当
l = r 时,唯一满足的点p = l ,输出Yes。
不难发现,其必要条件是
令
假设当前区间为
确认不会造成共链后,更新相应的权值即可。它的正确性利用了一个引理:
这个引理的证明可以通过对每一条边计算贡献得到。具体证明就是分类讨论,就不写了。注:如果只有两个点则一定满足,不需要前面三条条件。
因此这部分是可以做到
#include <iostream>
#include <vector>
using namespace std;
#define MAXN 100005
using ll = long long;
int n, q;
struct SegTree
{
struct Tree
{
ll s, t;
} t[MAXN << 2];
#define ls (p << 1)
#define rs (p << 1 | 1)
void pushup(int p)
{
t[p].s = t[ls].s + t[rs].s;
}
void pushdown(int p, int l, int r)
{
if (t[p].t)
{
int mid = (l + r) >> 1;
t[ls].s += (mid - l + 1) * t[p].t;
t[ls].t += t[p].t;
t[rs].s += (r - mid) * t[p].t;
t[rs].t += t[p].t;
t[p].t = 0;
}
}
void add(int p, int l, int r, int ql, int qr, ll v)
{
if (ql <= l && r <= qr)
{
t[p].s += v * (r - l + 1);
t[p].t += v;
return;
}
pushdown(p, l, r);
int mid = (l + r) >> 1;
if (mid >= ql)
{
add(ls, l, mid, ql, qr, v);
}
if (mid < qr)
{
add(rs, mid + 1, r, ql, qr, v);
}
pushup(p);
}
ll query(int p, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr)
{
return t[p].s;
}
pushdown(p, l, r);
int mid = (l + r) >> 1;
ll res = 0;
if (mid >= ql)
{
res += query(ls, l, mid, ql, qr);
}
if (mid < qr)
{
res += query(rs, mid + 1, r, ql, qr);
}
return res;
}
} T1, T2;
vector<int> e[MAXN];
int fa[MAXN], son[MAXN], sz[MAXN], dep[MAXN];
int top[MAXN], rnk[MAXN], dfn[MAXN], idx = 0;
void dfs1(int p, int f)
{
fa[p] = f;
sz[p] = 1;
dep[p] = dep[f] + 1;
for (int u : e[p])
{
if (u == f)
{
continue;
}
dfs1(u, p);
sz[p] += sz[u];
if (sz[u] > sz[son[p]])
{
son[p] = u;
}
}
}
void dfs2(int p, int t)
{
top[p] = t;
dfn[p] = ++idx;
rnk[idx] = p;
if (!son[p])
{
return;
}
dfs2(son[p], t);
for (int u : e[p])
{
if (u == fa[p] || u == son[p])
{
continue;
}
dfs2(u, u);
}
}
void addPath(int u, int v, int w, SegTree &t) // 我也不知道我为什么要写三遍 lca
{
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
{
swap(u, v);
}
t.add(1, 1, n, dfn[top[u]], dfn[u], w);
u = fa[top[u]];
}
if (dep[u] < dep[v])
{
swap(u, v);
}
t.add(1, 1, n, dfn[v], dfn[u], w);
}
ll queryPath(int u, int v, SegTree &t)
{
ll res = 0;
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
{
swap(u, v);
}
res += t.query(1, 1, n, dfn[top[u]], dfn[u]);
u = fa[top[u]];
}
if (dep[u] < dep[v])
{
swap(u, v);
}
return res + t.query(1, 1, n, dfn[v], dfn[u]);
}
int dis(int u, int v)
{
int res = dep[u] + dep[v];
while (top[u] != top[v])
{
if (dep[top[u]] < dep[top[v]])
{
swap(u, v);
}
u = fa[top[u]];
}
if (dep[u] < dep[v])
{
swap(u, v);
}
return res - 2 * dep[v];
}
int las[MAXN];
bool check(int l, int r)
{
if (l + 2 > r)
{
return true;
}
return !T1.query(1, 1, n, dfn[r], dfn[r]) &&
(queryPath(r - 1, r, T1) != T1.query(1, 1, n, dfn[r - 1], dfn[r - 1])) &&
(queryPath(r - 1, r, T2) == T2.query(1, 1, n, dfn[r - 1], dfn[r - 1]));
}
void add(int l, int r, int w)
{
T2.add(1, 1, n, dfn[r], dfn[r], w);
if (l && l <= n)
{
addPath(l, r, w, T1);
}
}
int main()
{
cin >> n >> q;
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1, 1);
dfs2(1, 1);
for (int l = 1, r = 0; l <= n; l++)
{
while (r < n && check(l, r + 1))
{
r++;
add(r - 1, r, 1);
}
las[l] = r;
add(l + 1, l, -1);
}
for (int i = 1; i <= q; i++)
{
int l, r;
cin >> l >> r;
if (l == r)
{
cout << "Yes" << endl;
}
else if (r > las[l])
{
cout << "No" << endl;
}
else if (r > l + 1 || dis(l, r) > 1)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
return 0;
}
Another Sol. by Crazyouth
题意:给定
题意转化证明见上。
首先我们以
若这些点互相没有祖先后代关系,显然不会有三点共链。
若有,则有两种情况会存在三点共链。不妨设
- 除
p 外的点不全在p 的一个儿子的子树内。此时p 的两个不同儿子的子树内的两个点与p 三点共链。 - 除
p 外的点仍有祖先后代关系,此时有祖先后代关系的点与p 全部共链。
其余情况下不会有三点共链。
考虑到直接求区间
所以只需对一个点集支持加点或删点并判断即可。那么我们分别看看上面这三个条件如何判断。
- 是否有祖先关系:求出对于一个点
i ,编号小于i 的最大编号的i 的祖先,与大于它的最小祖先是谁,这样就能快速判断区间是否有祖先关系。至于求法,在 dfs 时维护一个 set,到i 的时候使用lower_bound()找一下。 - 最浅的点:在双指针时也维护一个 set,记录
[l,r] 中点的深度,每次找第一个。 - 除最浅点以外的点是否有祖先关系:每次加点就把它的子树和到根路径的点权加一,并在此之前先判断这个点的点权是否大于等于
2 (因为除最浅点外有祖先关系等同于存在某个点既有一个后代也有一个祖先)。这里可以把子树点权与到根路径点权分开,分别使用树上差分求得。 - 除最浅点以外的点是否都位于它的一个儿子的子树内:此条件等价于除最浅点以外的点的最近公共祖先是最浅点的后代,反证不难。然后我们发现因为最浅点子树内如果没有点但外面有,那么由于它们存在祖先关系,所以它们加上这个最浅点一定共链,此时那些子树外的点的最近公共祖先不然浅于最浅点;若最浅点子树内有点,若它们不在最浅点的同一个儿子的子树内,则它们的最近公共祖先必然是最浅点的祖先(或最浅点本身),总结一下就是“除最浅点以外的点是否都位于它的一个儿子的子树内”等价于“除最浅点以外的点的最近公共祖先是否深于最浅点”。所以我们可以发现我们要做的只是求剩下的点的最近公共祖先,我们可以把区间拆为
[l,p-1] 与[p+1,r] (p 为最浅点编号),并求出两个区间的最近公共祖先然后合并。因为最近公共祖先满足结合律,所以可以使用线段树求一个区间的最近公共祖先。
至于判断相邻编号的点是否相邻,枚举一下每一条边并记录即可。
code by Crazyouth
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int fa[N][20],dep[N],lc[N<<2],fac[N],lac[N],ra[N],n,tr[N][2],dfn[N],mxdfn[N],dfncnt=0,vis[N];
vector<int> G[N],num[N];
set<pair<int,int>> stdep,acl;
set<pair<int,int>,greater<pair<int,int>>> acf;
set<int> st;
void upd(int k,int c,int t)//0 ancestor,1 descendant
{
while(k<=n)
{
tr[k][t]+=c;
k+=k&-k;
}
}
int qry(int k,int t)
{
int ret=0;
while(k)
{
ret+=tr[k][t];
k-=k&-k;
}
return ret;
}
void dfs(int u,int f)
{
if(st.size()&&st.lower_bound(u)!=st.end()) lac[u]=(*st.lower_bound(u));
else lac[u]=n+1;
if(st.size()&&st.lower_bound(u)!=st.begin()) fac[u]=(*(prev(st.lower_bound(u))));
st.insert(u);
dfn[u]=++dfncnt;
dep[u]=dep[f]+1;
fa[u][0]=f;
for(int i=1;i<=17;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
for(auto v:G[u])
{
if(v==f) continue;
dfs(v,u);
mxdfn[u]=max(mxdfn[u],mxdfn[v]);
}
st.erase(u);
}
int lca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
for(int i=17;~i;i--) if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
if(u==v) return u;
for(int i=17;~i;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
void build(int s=1,int t=n,int p=1)
{
if(s==t)
{
lc[p]=s;
return;
}
int m=s+t>>1;
build(s,m,p<<1);build(m+1,t,p<<1|1);
lc[p]=lca(lc[p<<1],lc[p<<1|1]);
}
int qlca(int l,int r,int s=1,int t=n,int p=1)
{
if(l<=s&&t<=r) return lc[p];
int m=s+t>>1;
if(l<=m&&r>m) return lca(qlca(l,r,s,m,p<<1),qlca(l,r,m+1,t,p<<1|1));
if(l<=m) return qlca(l,r,s,m,p<<1);
return qlca(l,r,m+1,t,p<<1|1);
}
inline void add(int pt)
{
acf.insert({fac[pt],pt});
acl.insert({lac[pt],pt});
stdep.insert({dep[pt],pt});
}
inline void del(int pt)
{
acf.erase({fac[pt],pt});
acl.erase({lac[pt],pt});
stdep.erase({dep[pt],pt});
}
int main()
{
int q;
cin>>n>>q;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=n;i++)
{
for(auto j:G[i])
if(j==i+1) vis[i]=1;
}
dfs(1,0);
build();
int pt=1;
for(int i=1;i<=n;i++)
{
while(pt<=n)
{
add(pt);
int now=stdep.begin()->second;
if(acf.begin()->first>=i||acl.begin()->first<=pt)
{
if(qry(mxdfn[pt],0)-qry(dfn[pt]-1,0)+qry(dfn[pt],1)>1)
{
del(pt);
ra[i]=--pt;
break;
}
int nlca;
if(i==pt) goto E;
else if(now==i) nlca=qlca(i+1,pt);
else if(now==pt) nlca=qlca(i,pt-1);
else nlca=lca(qlca(i,now-1),qlca(now+1,pt));
if(dep[nlca]<=dep[now])
{
del(pt);
ra[i]=--pt;
break;
}
E:;
}
upd(dfn[pt],1,0);upd(dfn[pt],1,1);upd(mxdfn[pt]+1,-1,1);
pt++;
}
if(pt==n+1) ra[i]=n+1;
del(i);
upd(dfn[i],-1,0);upd(dfn[i],-1,1);upd(mxdfn[i]+1,1,1);
}
while(q--)
{
int l,r;
cin>>l>>r;
cout<<(vis[l]&&r==l+1?"No":(r<=ra[l]?"Yes":"No"))<<'\n';
}
}
D
sol
我们可以对
因为
-
\sum_{k=1}^V\left(\sum_{v \text{ is a permutation of }[M]}\sum_{j=1}^N[v_k(j) = j]\right) -
\sum_{k=1}^V\left(\sum_{v \text{ is a permutation of }[M]}\sum_{j=1}^N[v_k(j) = 2j]\right)
由期望的线性性,所有合理范围内的
第一个式子
定义排列
那么,当
则,我们可以枚举环长
除去
第二个式子
我们只需要
当
当
std
#include <iostream>
using namespace std;
#define MOD 998244353ll
using ll = long long;
ll pow(ll b, ll p, ll m)
{
b %= MOD;
ll r = 1;
while (p)
{
if (p & 1)
{
r = r * b % m;
}
b = b * b % m;
p >>= 1;
}
return r;
}
ll inv(ll p)
{
return pow(p, MOD - 2, MOD);
}
int main()
{
int t;
cin >> t;
while (t--)
{
ll n, m, v;
cin >> n >> m >> v;
ll s = 0;
for (ll l = 1, r; l <= m && l <= v; l = r + 1)
{
r = min(m, v / (v / l));
s = (s + (r - l + 1) % MOD * (v / l % MOD) % MOD) % MOD;
}
ll mid = m / n;
ll res = (mid - 1) % MOD * (n % MOD) % MOD;
for (ll l = mid + 1, r; l <= m; l = r + 1)
{
r = m / (m / l);
res = (res + (r - l + 1) % MOD * (m / l % MOD) % MOD) % MOD;
}
cout << (res * (((m % MOD) * (v % MOD) % MOD - s + MOD) % MOD) % MOD * inv(m % MOD * ((m - 1) % MOD) % MOD) % MOD + n % MOD * inv(m % MOD) % MOD * s % MOD) % MOD << endl;
}
return 0;
}
E
sol
我们先转换题意。令
令
给定
l, r ,求一组(l', r') 使得[l', r'] \sub [l, r] 且h_{l'} = h_{r'} = \min_{i \in [l', r']}h_i ,并且最大化r' - l' 。
性质一:最优解必然有一个端点
证明:考虑反证,令最优解为
于是我们可以分类讨论。
如果只有一个端点
考虑两个端点都
这两类并不能涵盖所有两个端点
令
当然,如果要求在线的话,用主席树一样可以做到
std
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
#define MAXN 1000006
#define ls (p << 1)
#define rs (p << 1 | 1)
using ll = long long;
int a[MAXN];
ll h[MAXN], s[MAXN], b[MAXN];
int idx;
int n, m;
struct Query
{
int l, r, i;
} q[MAXN];
ll res[MAXN];
struct Node
{
int p;
ll v;
int u;
} qr[MAXN << 4];
int la[MAXN];
int qid = 0;
void add(int u, int p, ll v)
{
qr[++qid] = {p, v, la[u]};
la[u] = qid;
}
vector<int> ps[MAXN];
template <typename Cmp = less<ll>, ll st = 0>
struct SGT
{
static Cmp cmp;
struct Tree
{
ll m, t;
} t[MAXN << 2];
void pushup(int p)
{
t[p].m = max(t[ls].m, t[rs].m, cmp);
}
void pushdown(int p)
{
if (t[p].t != st)
{
t[ls].m = max(t[ls].m, t[p].t, cmp);
t[ls].t = max(t[ls].t, t[p].t, cmp);
t[rs].m = max(t[rs].m, t[p].t, cmp);
t[rs].t = max(t[rs].t, t[p].t, cmp);
t[p].t = st;
}
}
void build(int p, int l, int r)
{
t[p] = {st, st};
if (l == r)
{
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
}
void modi(int p, int l, int r, int q, ll v)
{
if (l == r)
{
t[p].m = max(t[p].m, v, cmp);
t[p].t = max(t[p].t, v, cmp);
return;
}
pushdown(p);
int mid = (l + r) >> 1;
if (mid >= q)
{
modi(ls, l, mid, q, v);
}
else
{
modi(rs, mid + 1, r, q, v);
}
pushup(p);
}
ll query(int p, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr)
{
return t[p].m;
}
pushdown(p);
int mid = (l + r) >> 1;
ll res = st;
if (mid >= ql)
{
res = max(res, query(ls, l, mid, ql, qr), cmp);
}
if (mid < qr)
{
res = max(res, query(rs, mid + 1, r, ql, qr), cmp);
}
return res;
}
};
SGT<> t1;
SGT<greater<ll>, MAXN> t2;
struct Stack
{
int s[MAXN];
int h = 0;
inline void push_back(int v)
{
s[++h] = v;
}
inline bool empty()
{
return !h;
}
inline int back()
{
return s[h];
}
inline void pop_back()
{
h--;
}
inline void clear()
{
h = 0;
}
} st;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
for (int i = 1; i <= n; i++)
{
char ch;
cin >> ch;
if (ch == '(')
{
h[i] = h[i - 1] + a[i];
}
else
{
h[i] = h[i - 1] - a[i];
}
b[i + 1] = h[i];
}
for (int i = 1; i <= m; i++)
{
cin >> q[i].l >> q[i].r;
q[i].l--;
q[i].i = i;
}
sort(q + 1, q + m + 1, [](Query a, Query b)
{ return a.r < b.r; });
// part 1 & 2: 2d counting
st.push_back(0);
for (int i = 1; i <= n; i++)
{
// h[has] >= h[cur] => pop
while (!st.empty() && h[st.back()] >= h[i])
{
st.pop_back();
}
if (!st.empty())
{
int cur = st.back();
add(i, cur, s[i] - s[cur] - h[i] + h[cur]);
}
st.push_back(i);
}
st.clear();
st.push_back(0);
for (int i = 1; i <= n; i++)
{
// h[las] > h[cur] => pop
while (!st.empty() && h[st.back()] > h[i])
{
st.pop_back();
}
if (!st.empty() && h[st.back()] == h[i])
{
int cur = st.back();
add(i, cur, s[i] - s[cur]);
}
else
{
st.push_back(i);
}
}
st.clear();
st.push_back(n);
for (int i = n - 1; ~i; i--)
{
// h[las] > h[cur] => pop
while (!st.empty() && h[st.back()] >= h[i])
{
st.pop_back();
}
if (!st.empty())
{
int cur = st.back();
add(cur, i, s[cur] - s[i] - h[i] + h[cur]);
}
st.push_back(i);
}
st.clear();
st.push_back(n);
for (int i = n - 1; ~i; i--)
{
// h[las] > h[cur] => pop
while (!st.empty() && h[st.back()] > h[i])
{
st.pop_back();
}
if (!st.empty() && h[st.back()] == h[i])
{
int cur = st.back();
add(cur, i, s[cur] - s[i] - h[i] + h[cur]);
}
else
{
st.push_back(i);
}
}
t1.build(1, 0, n);
int p = 1;
for (int i = 0; i <= n; i++)
{
for (int j = la[i]; j; j = qr[j].u)
{
auto [l, v, u] = qr[j];
t1.modi(1, 0, n, l, v);
}
while (p <= m && q[p].r == i)
{
res[q[p].i] = max(res[q[p].i], t1.query(1, 0, n, q[p].l, i));
p++;
}
}
// part 3: bs on vector & sgt
sort(b + 1, b + n + 2);
idx = unique(b + 1, b + n + 2) - b - 1;
t2.build(1, 0, n);
for (int i = 0; i <= n; i++)
{
h[i] = lower_bound(b + 1, b + idx + 1, h[i]) - b;
ps[h[i]].push_back(i);
t2.modi(1, 0, n, i, h[i]);
}
for (int i = 1; i <= m; i++)
{
auto [l, r, j] = q[i];
int m = t2.query(1, 0, n, l, r);
int fi = *lower_bound(ps[m].begin(), ps[m].end(), l);
int la = *lower_bound(ps[m].rbegin(), ps[m].rend(), r, greater<int>());
res[j] = max(res[j], s[la] - s[fi]);
}
// output
for (int i = 1; i <= m; i++)
{
cout << res[i] << '\n';
}
return 0;
}
F
sol
先说结论:极差最大只有
令
由于太多根号了码起来麻烦,为了简写,
下面的方法给出了如何构造一组解。
\Delta \le 1
当
当
如果
因此
1) v \equiv 0 \pmod 2
此时
此时
由
可以观察到,这两个区间都很小(其中的整点数
\Delta = 3
仅有 v, (v + 1), (v + 3)
此时
仅有 v, (v + 2), (v + 3)
此时
四个数均有
注:后面发现,在数据范围内,前面几种已经完全够了,即不需要这种情况。std 与后面的证明均不会提到此情况。
这个时候就不是
\Delta = 2
此时有
由
由
因此
仅有 v, (v + 1), (v + 3)
此时有
由
由
仅有 v, (v + 2), (v + 3)
此时有
则有: