0x00 基本算法
0.1 位运算
运算 | 符号 |
---|---|
与 | and,& |
或 | or,| |
非 | not,~ |
异或 | xor,^ |
tips:在m位二进制数中,方便起见,通常称最低位为第0位,在高位为m-1位,依次类推。
0.1.1 补码
32位无符号整数(unsigned int):直接把32位编码C作为32位二进制数N
32位有符号整数(int):以最高位为符号位
补码:-x = ~x+1 (反码+1)
32位补码表示 | unsigned int | int |
---|---|---|
000000……000000 | 0 | 0 |
011111……111111 | 2147483647 | 2147483647 |
100000……000000 | 2147483648 | -2147483648 |
111111……1111111 | 4294967295 | -1 |
tips:0x3F3F3F3F常用于将数值初始化为正无穷
- 0x3F3F3F3F * 2 < 0x7FFFFFFF ,即加上一个小于它的数不会导致溢出,在图论中可以避免加法算术上溢和繁琐的判断
- 整数的每8位字节相同 (memset(a,val,sizeof(a))只能赋值每8位相同的int )
0.1.2 移位运算
左移:在二进制表示下把数字同时向左移动,低位以0填充,高位越界后舍弃。
算术右移:在二进制补码表示下数字向右移动,高位以符号位填充,低位越界后舍弃
n>>1 = [n/2.0](向下取整)
0.1.3 二进制状态压缩(待补充)
0.1.4 成对变换
对于非负整数 n,
当n为整数时:n xor 1 等于 n+1
当n为奇数时:n xor 1 等于 n-1
因此“0与1”,“2与3”,“4与5”……关于 xor1运算构成成对变换,即可用异或来实现配偶
这一性质经常用于图论邻接表中边集的存储。
0.1.5 lowbit 运算
lowbit运算:非负整数 n 在二进制表示下最低位的 1 及其后边所有的 0 构成的数值。
公式:lowbit(n)=n&(~n+1)=n&(-n) -n:n的补码
0.2 递推和递归
0.2.1 递推与递归的宏观描述
递推:以已知的问题边界为起点向原问题正向推导的扩展方式
递归:以原问题为起点尝试寻找把状态空间缩小到已知的问题边界的路线
递归操作的关键点
自身调用自身:求解原问题规模缩小以后的子问题
回溯是还原现场:求解子问题失败,程序重新回到当前问题寻找其他的变换路线
例题:POJ1598 四座汉诺塔
#include<iostream>
#include<cstring>
using namespace std;
int d[15];
int f[15];
int main(){
d[1] = 1;
for (int i = 2; i <= 12; i++){
d[i] = d[i - 1] * 2 + 1;
}
memset(f, 0x3f3f3f, sizeof(f));
f[0] = 0;
for (int i = 1; i <= 12; i++){
for (int j = 0; j < i; j++){
f[i] = min(f[i], f[j] * 2 + d[i - j]);
}
}
for (int i = 1; i <= 12; i++)
cout << f[i] << endl;
}
0.2.2 分治
分治:把一个问题划分为若干个规模更小的同类子问题,对这些子问题递归求解,然后在回溯时通过它们推导出原问题的解。
例题 :POJ1845
#include<iostream>
#include<cstring>
using namespace std;
const int mod = 9901;
int ksm(int a,int b){
if(!a) return 0;
a %= mod;
int sum = 1;
while(b){
if( b&1 ) sum = (sum*a) % mod;
b = b >> 1;
a = (a * a) % mod;
}
return sum;
}
int sum(int p, int c){
if( c == 0) return 1;
if( c%2 == 0 )
return ((1 + ksm(p, c / 2))*sum(p, c/2-1)+ksm(p,c)) %mod;
else
return ((1+ksm(p,(c+1)/2))*sum(p,(c-1)/2)) %mod;
}
int main(){
int a, b;
/*
while(cin >> a >> b){
cout << sum(a, b) << endl;
}*/
cin >> a >> b;
int res = 1;
for (int p = 2; p <= a; p++){
int c = 0;
while( a%p==0 ){
a /= p;
cpp;
}
if(c) res = (res*sum(p, c*b)) %mod;
}
if( !a ) res = 0;
cout << res << endl;
}
例题:POJ3714 平面最近点对
题意:给出a阵营n个点和b阵营n个点,求两个阵营最短距离
解法:分治+归并排序
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#include<cstdio>
using namespace std;
const int maxn = 200010;
const int INF = 1e9;
struct Point{
double x, y;
bool type;
bool operator< (const Point &W)const{
return x < W.x;
}
};
Point points[maxn], temp[maxn];
double dist(Point a,Point b){
if( a.type == b.type)
return INF;
double dx = a.x - b.x, dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
double dfs(int l,int r){
if( l>=r )
return INF;
int mid = (l + r)>>1;
double mid_x = points[mid].x;
double res = min(dfs(l, mid), dfs(mid + 1, r));
//归并排序
{
int k = 0, i = l, j = mid + 1;
while( i<=mid && j<=r)
if( points[i].y < points[j].y )
temp[k++] = points[i++];
else
temp[k++] = points[j++];
while( i<=mid )
temp[k++] = points[i++];
while( j<=r )
temp[k++] = points[j++];
for (i = 0, j = l; i<k; i++, j++)
points[j] = temp[i];
}
int k = 0;
for (int i = l; i <= r; i++){
if( points[i].x >= mid_x-res && points[i].x <=mid_x+res)
temp[k++] = points[i];
}
for (int i = 0; i < k; i++)
for (int j = i - 1; j >= 0 && temp[i].y - temp[j].y < res; j--)
res = min(res, dist(temp[i], temp[j]));
return res;
}
int main()
{
int T, n;
cin >> T;
while (T -- )
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
scanf("%lf%lf", &points[i].x, &points[i].y);
points[i].type = 0;
}
for (int i = n; i < 2 * n; i ++ )
{
scanf("%lf%lf", &points[i].x, &points[i].y);
points[i].type = 1;
}
sort(points, points + n * 2);
printf("%.3lf\n", dfs(0, n * 2 - 1));
}
return 0;
}
0.2.3 分形
确定分图形坐标+dfs
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
using namespace std;
const int maxn = 10100;
int mp[maxn][maxn];
void sol(int x,int y,int cur){
if( cur==1 )
mp[x][y] = 1;
else{
int d = pow(3, cur - 2);
sol(x, y, cur - 1);
sol(x + 2 * d, y, cur - 1);
sol(x + d, y + d, cur - 1);
sol(x, y + 2 * d, cur - 1);
sol(x + 2 * d, y + 2 * d, cur - 1);
}
}
int main(){
ios::sync_with_stdio(false);
int n;
cin >> n;
sol(0, 0, 7);
while( n != -1 ){
int len = pow(3, n - 1);
for (int i = 0; i < len; i++){
for (int j = 0; j < len; j++){
if( mp[i][j] )
cout << "X";
else
cout << " ";
}
cout << endl;
}
cout << "-" << endl;
cin >> n;
}
}
0.2.4 递归的非递归表示
使用模拟的方法,使用一个数组模拟栈。
#include<cstdio>
#include<iostream>
#include<stack>
using namespace std;
int n, m;
struct State{
int pos;
int u;//遍历位置
int sum;//已选择的数
int state;//遍历数的状态
};
//这一段函数未被调用,只作为说明pos对应的程序段
void dfs(int u,int sum,int state){
//0:
if(sum + n - u < m) return;
if(sum == m){
for (int i = 0; i < n; i++)
if(state >> i&1 )
cout << i + 1 << ' ';
cout << endl;
return;
}
dfs(u + 1, sum + 1, state | 1 << u);
//1:
dfs(u + 1, sum, state);
//2
}
int main(){
stack<State> stk;
stk.push({0,0,0,0,});
cin >> n >> m;
while( stk.size() ){
State t = stk.top();
stk.pop();
switch (t.pos){
case 0:
if(t.sum + n - t.u < m )
continue;
if( t.sum == m ){
for (int i = 0; i < n; i++)
if(t.state >> i&1 )
cout << i + 1 << ' ';
cout << endl;
continue;
}
t.pos = 1;
stk.push(t);
stk.push({0,t.u + 1, t.sum + 1, t.state | 1 << t.u});
break;
case 1:
stk.push({0,t.u + 1, t.sum, t.state});
break;
case 2:
stk.pop();
}
}
}
0.3 前缀和与差分
0.3.1 前缀和
对于一个给定的数列A,它的前缀和数列S
S[i]=A[1]+A[2]+A[3]+…+A[4]
一个部分和,即数列下标区间内的数的和,可表示为前缀和相减的形式
sum(l,r) = sum[r] - sum[l-1]
0.3.2 二维前缀和
设目标矩形的四个点坐标为(x,y)(x,j)(i,y)(i,j)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JAI2SG1v-1616723631857)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1596532771982.png)]
S=sum[i] [j]-sum[i] [y]-sum[x] [j]+sum[x] [y]
例题:BZOJ218
#include<iostream>
using namespace std;
const int maxn = 5010;
int g[maxn][maxn];
int main(){
int N, R;
cin >> N >> R;
int n = R, m = R;
for(int i = 0; i < N; i++){
int x, y, w;
cin >> x >> y >> w;
x++, y++;//起点坐标从1开始
n = max(n, x), m = max(m, y);
g[x][y] += w;
}
//构建前缀和
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
g[i][j] += g[i - 1][j] + g[i][j - 1] - g[i-1][j-1]; //原先每个点已赋值
//遍历区域右下角的点
int res = 0;
for (int i = R; i <= n; i++)
for (int j = R; j <= m; j++)
res = max(res, g[i][j]-g[i-R][j] - g[i][j-R] + g[i-R][j-R]);
cout << res << endl;
}
0.3.3 差分
对于一个给定数列A,它的差分数列B定义为:
B[1] = A[1],B[i] = A[i] - A[i-1] (2 <= i <= n)
由此可见,前缀和与差分是一组互逆运算
利用差分可以把原序列上的 区间操作 转化为差分序列上的 单点操作
例题:CH0304
给定一个序列,每次可选择一个区间+1或-1,求至少需要多少次操作才能使数列中所有数一样,并求出最少次数的方案数
我们可以把区间操作转换为差分序列b[n]的操作,当b2……bn全为0时满足题目条件。当对区间 [i,j] 进行操作时,相当于选择了一对 bi bj+1一个+1,一个-1.
设b[n]正数和为p,负数和为q,那么可执行 min(p,q)次使正负和抵消一部分,剩下正数或负数的差分和 |p-q|
然后可将剩下的值选择与 b1 (产生|p-q|种序列)或 bn+1(产生1种序列) 配对,即 |p-q|+1种方案数
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 100010;
int a[maxn];//原序列
int b[maxn];//差分
int main(){
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = n; i > 1; i--)
b[i] = a[i] - a[i - 1];
b[1] = a[1];
//统计差分
LL pos = 0;//差分正值和
LL neg = 0;//差分负值和
for (int i = 2; i<=n; i++)
if(b[i]>0)
pos += b[i];
else
neg -= b[i];
cout << min(pos, neg) + abs(pos - neg) << endl;
cout << abs(pos - neg) + 1 << endl;
}
0.4 二分
0.4.1 整数集合上的二分
对于闭区间[l,r],整数集合上的二分有两种情况
①在单调递增序列中查找 ≥x 的数中最小的一个(x或x的后继)
mid = (l+r)/2 区间划分为 [l,mid] 和 [mid+1,r]
while (l<r){
int mid = (l+r) >> 1;
if( a[mid] >=x ) r = mid;
else l = mid + 1;
}
return a[l];
②在单调递增序列中查找 ≤x 的数中最小的一个(x或x的前驱),
mid = (l+r+1)/2 区间划分为 [l,mid-1] 和 [mid,r]
while(l<r){
int mid =(l+r+1) >> 1;
if( a[mid] <= x) l=mid;
else r = mid-1;
}
return a[l];
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CkiMXalA-1616723631869)(D:\ipanda\ACM\library\pictures\IMG_20200815_102358.jpg)]
Tips:
-
右移运算是向下取整,而整数除法是向零取整,在二分值域包含负数时整数除法不能正常工作
-
(l+r)>>1 不会取到 r 这个值 ,(l+r+1)>>1 不会取到 l 这个值 可以利用这一性质处理无解的情况:
把最初的二分区间 [1,n] 分别扩大为 [1,n+1] 和 [0,n]
0.4.2 实数域上的二分
实数域上二分与整数域不同的是需要确定好精度 eps,以 l+eps < r 为循环条件,每次根据在mid上的判定选择
r = mid 或 l = mid 分支之一即可。
一般需要保留 k 位小数时,取 eps = 10 -(k+2)
while( l+eps < r ){
double mid = (l+r)/2;
if( check(mid) ) r = mid;
else l = mid;
}
也可以采用循环固定次数的二分方法
for(int i=0; i<100; i++){
doubel mid = (l+r)/2;
if( check(mid) ) r=mid ;
else l =mid;
}
0.4.3 三分法求单峰值函数的极值
以单峰函数 f 为例,我们在函数定义域 [l,r] 上任取两个点 lmid 和 rmid ,把函数分为三段
-
若 f(lmid) < f(rmid), 令 l=lmid
-
若 f(lmid) > f(rmid), 令 r=rmid
while( l+eps < r ){
double lmid = l+(r-l)/3;
double rmid = r-(r-l)/3;
if( f(1mid) < f(rmid)) l = lmid;
else r = rmid;
}
如果函数不严格单调,即在函数中存在一段值相等的部分,那么无法判断定义域的左右边界如何缩小,三分法不再适用
0.4.4 二分答案转化为判定
借助二分,我们把求最优解的问题转化为给定一个 mid ,判定是否存在一个可行方案评分达到 mid 的问题。
//把n书分成m组,每组厚度之和<=size,是否可行
bool valid(int size){
int group = 1, rest = size;
for (int i = 1; i <= n;i++){
if(rest >=a[i] )
rest -= a[i];
else
group++, rest = size - a[i];
}
}
int main(){
//二分答案,判定每组厚度之和不超过二分的值时能否在m组内把书分完
int l = 0, r = sum_of_ai;
while (l<r){
int mid = (l + r) / 2;
if( vaild(mid) )
r = mid;
else
l = mid + 1;
}
cout << l << endl;
}
例题:POJ2018
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn = 100010;
int n, m;
int a[maxn];
double sum[maxn];
bool check(double avg){
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i] - avg;
//将前缀和每一项减去平均数,转换判定为是否存在一个子段非负
double minv=1e10;
for (int i = m; i <= n; i++){
minv = min(minv, sum[i - m]);
if( sum[i]>minv)
return true;
}
return false;
}
int main(){
cin >> n >> m;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
double l = 1;
double r = 2000;
while( l+1e-5 < r){
double mid = (l + r) / 2;
if( check(mid) ) l = mid;
else r = mid;
}
printf("%d", (int)(1000 * r));
}
0.5 排序
0.5.1 离散化
void discrete(){
sort(a+1,a+n+1);
for(int i=1; i<=n; i++)
if(i==1 || a[i]!=a[i-1])
b[++m] =a[i];
}
int query(int x){
return lower_bound(b+1,b+m+1, x) - b;
}
0.5.2 中位数
模型1:货仓选址
在X轴上有N个商店,其位置位xi(1<i<N),现需要求将货仓在X轴上某一 点,求货仓建在何处时使得货仓到各商店距离之和最小。
货仓应建在中位数处,即把A排序后,当N为奇数,货仓建在A[(N+1)/2],当N为偶数时建在A[N/2] ~ A[N/2+1]之间的任何位置。
模型2:均分纸牌
有N堆纸牌,每堆上有若干张,但纸牌总数必为N的倍数。可以在任一堆上取一张张纸牌,然后移动到相邻的一堆中,求至少要多少步操作才能让每个人受中共持有的纸牌数相等。
因为第一堆纸牌只能与第二堆进行操作,所以我们先考虑第一堆从第二堆拿来或拿去|C[1]-T/N |,T为纸牌的总数。以此方式考虑第二堆、第三堆,在这过程中即使某个C[i]变成负数也没关系,因为最终结果每堆都会变成 T/M张。
我们设 A[i] =C[i] -T/M,那么答案就是A[i]的前缀和之和的绝对值。sum|S[i]|
#include<iostream>
using namespace std;
const int maxn = 1000010;
typedef long long LL;
int a[maxn];
int s[maxn];
int main(){
ios::sync_with_stdio(false);
int n;
int ans=0;
LL t = 0;
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
t += a[i];
}
t /= n;
for (int i = 1; i <= n; i++){
a[i] -= t;
s[i] = s[i - 1] + a[i];
ans += s[i];
}
if( ans<0){
ans = -ans;
}
cout << ans << endl;
}
模型3:糖果传递(环形均分纸牌)
有n个小朋友坐成一圈,每人有若干颗糖果,每人只能给左右两人传递糖果。每人每次传递一颗糖果的代价为1。求使所有人获得均等糖果的最小代价。
与均分纸牌不同的是,我们要确定一个k,在第k个人之后把环断开形成一条链,因此要让 k 与 k+1 位置上没有发生变换。此时 k+1 作为第一个元素 A数组与前缀和为:
A | S |
---|---|
A[k+1] | s[k+1]-s[k] |
A[k+2] | s[k+2]-s[k] |
…… | …… |
A[M] | s[M]-s[k] |
A[1] | s[1]+s[M]-s[k] |
…… | …… |
A[k] | s[k]+s[M]-s[k] |
因为均分之后A数组每个数都为0,因此S[M]=0;
此时的答案为 sum|s[i]-s[k]|.
另外,k的值可以通过“货仓选址”选择,取中位数作为 s[k] 就能得到最优解。
例题:https://ac.nowcoder.com/acm/problem/50172
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1000010;
typedef long long LL;
LL a[maxn];
LL s[maxn];
int main(){
ios::sync_with_stdio(false);
int n;
LL ans=0;
LL t = 0;
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
t += a[i];
}
t /= n;
for (int i = 1; i <= n; i++){
a[i] -= t;
s[i] = s[i - 1] + a[i];
}
sort(s+1, s + n+1);
int k = (n + 1) / 2;
for (int i = 1; i <= n; i++){
ans += abs(s[i] - s[k]);
}
cout << ans << endl;
}
0.5.3 第k大数
当我们求第k大数时,可以使用快速排序的思想,选取一个基准值后统计大于基准值的数量cnt,如果k<=cnt,那我们就在小于等于基准值的数中寻找第k大数,反之就在大于等于基准值的数中寻找第 k-cnt 大数
0.5.4 逆序对
求逆序对可以用归并排序的方法。
#include<iostream>
using namespace std;
const int maxn = 500010;
int a[maxn];
int b[maxn];
int cnt;
void merge(int l,int mid,int r){
//合并a[l~mid] 与 a[mid+1~r]
//a待排数组 b临时数组 cnt逆序对个数
int i = l, j = mid + 1;
for (int k = l; k <= r; k++){
if( j>r || i<=mid && a[i] <=a[j] ){
b[k] = a[i++];
}else{
b[k] = a[j++];
cnt += mid - i + 1;
}
}
for (int k = l; k <= r; k++){
a[k] = b[k];
}
}
void merge_sort(int l,int r){
if(l>=r)
return;
int mid=(l+r)/2;
merge_sort(l,mid);
merge_sort(mid+1,r);
merge(l,mid,r);
}
int main(){
ios::sync_with_stdio(false);
int n;
int d = 2;
cin >> n;
while(n){
cnt =0;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
merge_sort(1, n);
cout << cnt << endl;
cin >> n;
}
}
0.6 倍增
例题:给定一个序列,然后进行若干次询问,每次给定一个整数T,求满足 sum(A[i]~A[k])<=T的最大k值
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int n = 10;
int a[n+1] = {0 ,1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int s[n+1];
int main(){
int T;
//预处理前缀和
for (int i = 1; i <=10;i++){
s[i] = s[i - 1] + a[i];
}
while( cin >> T ){
int p = 1;
int k = 0;
int sum = 0;
while(p){
if(k+p>n)
p = n - k;//边界判定
if( sum+s[k+p]-s[k]<=T ){
sum += s[k + p] - s[k];
k = k + p;
p *= 2;//满足条件,倍数翻倍
}else{
p /= 2;//不满足条件倍数减半
}
}
cout << k << endl;
}
}
ST算法
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int n = 10;
const int maxn = 1e3;
int a[n+5] = {0 ,1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int f[maxn][maxn];//±íʾÇø¼ä[i,i+2^j-1]ÀïÊýµÄ×î´óÖµ
void ST_prework(){
for (int k = 1; k <= n; k++){
f[k][0] = a[k];
}
int t = log(n) / log(2) + 1;//jµÄ×î´óÖµ
for (int j = 1; j < t; j++){
for (int i = 1; i <= n - (1 << j) + 1;i++){
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
}
}
int ST_query(int l,int r){
int k = log(r - l + 1) / log(2);
return max(f[l][k], f[r - (1 << k) + 1][k]);
}
//Çø¼äÇó×îÖµ
int main(){
int l;
int r;
ST_prework(); //STÔ¤´¦Àí
while( cin >> l >> r ){
cout << ST_query(l, r)<<endl;
}
}
0.7 贪心
例题:畜栏预订POJ3190
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef pair<int, int> p;
const int maxn = 50010;
int n;
int id[maxn];
pair<p, int> cows[maxn];
int main(){
cin >> n;
for (int i = 0; i < n; i++){
cin >> cows[i].first.first >> cows[i].first.second;//进出时间
cows[i].second = i;//编号
}
sort(cows, cows + n);
priority_queue<p, vector<p>, greater<p>> heap;
for (int i = 0; i < n; i++){
//所有畜栏的最后一头牛结束吃草的时间是否比当前牛吃草时间早
if( heap.empty() || heap.top().first >= cows[i].first.first ){
p stall = {cows[i].first.second, heap.size()};//新建一个畜栏
id[cows[i].second] = stall.second;
heap.push(stall);
}else{
auto stall = heap.top();//把牛置入堆顶的畜栏
heap.pop();
stall.first = cows[i].first.second;
id[cows[i].second] = stall.second;
heap.push(stall);
}
}
cout << heap.size() << endl;
for (int i = 0; i < n; i ++ ) cout << id[i] + 1 << endl;
}
例题:雷达定位 POJ1328
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
typedef pair<double, double> Pair;
const int maxn = 1010;
Pair a[maxn];//点坐标
Pair b[maxn];//区间范围
bool cmp(Pair a,Pair b){
return a.second < b.second;
}
int main(){
int n, d;
while( cin >> n >> d && n+d){
double x, y;
int flag = 1;
d *= d;
for (int i = 0; i < n; i++){
cin >> x >> y;
a[i].first = x;
a[i].second = y;
y *= y;
if( y>d )
flag = 0;
b[i].first = x - sqrt(d-y);
b[i].second = x + sqrt(d - y);
}
if(flag){
sort(b, b + n,cmp);
int cnt = 0;
double last = 0;
for (int i = 0; i < n; i++){
if( !i || !(b[i].first <=last ) ){
last = b[i].second;
cnt++;
}
}
cout << cnt << endl;
}else{
cout << -1 << endl;
}
}
}