分块
分块思想
-
我们一般把m个元素分为一块,所以总共的块数就是n/m块, 一般情况下,我们取m=sqrt(n)。
-
对于区间加操作,可以先对两边进行暴力处理,然后对中 间的整块的部分进行加减,我们累加在块的标记上,然后我们每次查询的时候只要每个元素的值加上这个块的标记值,就可以得到我们的答案了
-
简单来说区间加
暴力的方法处理不完整的块
完整块标记法
A . 数列分块入门1
思路
首先对一个序列进行分块,假设序列为:1,2,3,4,5,6,7,8,9。那么每一个块的大小为3,所以,123属于第一块,456为第二块,789为第三块。然后用一个数组pos[]去记录第i个元素所处于哪个块内。例如:pos[2]=1,即2这个元素位于第一块。
假设块的大小为len,那pos[i]=(i-1)/len+1。
这样我们就把原序列分成n/len(+1)个块了,每次区间操作的时候,不在一整块的元素暴力循环一下,在一个整块的,就开一个数组t[]记录第i块还没有加的数是多少。
注
注意下l,r在同一块的处理
Code:
/*
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =5e5+7;
int a[maxn];
int pos[maxn];
int mmax[maxn];
int t[maxn];
int len;
void f(int l,int r,int c) {
for(int i=l; i<=min(pos[l]*len,r); i++) {//处理左边的边角块
a[i]+=c;
}
if(pos[l]!=pos[r]) {//处理右边的边角块
for(int i=r; i>=max((pos[r]-1)*len+1,l); i--) {
a[i]+=c;
}
}
for(int i=pos[l]+1; i<=pos[r]-1; i++) { //处理中间剩余的整块
t[i]+=c;
}
}
int main() {
int n;
cin>>n;
len=sqrt(n);//分成的每个块的大小,一般长度都为sqrt(n)
for(int i=1; i<=n; i++) {
cin>>a[i];
pos[i]=(i-1)/len+1;//表示第i个数字在第几块里
}
for(int i=1; i<=n; i++) {
int opt,l,r,c;
cin>>opt>>l>>r>>c;
if(opt==0)
f(l,r,c);
else
cout<<a[r]+t[pos[r]]<<endl;
}
return 0;
}
B . 数列分块入门 2
思路
原链接
区间加法 + 区间查询
具体看代码解释
注
-
由于在暴力修改不完整的块时,破坏了块原来的单调性,所以需要另外建立一个数组,对其进行重新赋值,并进行排序
-
对于完整块的修改,使用二分
二分计算方法回顾
原文链接
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
在从小到大的排序数组中,
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
在从大到小的排序数组中,重载lower_bound()和upper_bound()
lower_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
Code:
/*
利用二分查找的方法在一个排好序的数组中进行查找
lower_bound( begin,end,num)
从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字
找到返回该数字的地址,不存在则返回end
通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
using namespace std;
typedef unsigned long long ull;
typedef pair<int, int>PII;
const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =1e6+7;
int n;
int l[maxn];//第i块左区间端点
int r[maxn];//第i块右区间端点
int pos[maxn];//第i个元素位于第几块
int a[maxn],b[maxn];
int opt,ll,rr,c;
int vis[maxn];
void f(int L,int R,int C){
int pl=pos[L];//左端点所在的块数
int pr=pos[R];//右端点所在的块数
if(pl==pr){
for(int i=L;i<=R;i++){
a[i]+=C;//可能破坏单调性
}
for(int i=l[pl];i<=r[pl];i++){
b[i]=a[i];//b数组重新赋值
}
sort(b+l[pl],b+1+r[pl]);//重新排序
}
else{
for(int i=L;i<=r[pl];i++){//修改左侧不完整的块
a[i]+=C;
}
for(int i=l[pl];i<=r[pl];i++){//b数组进行重新赋值
b[i]=a[i];
}
sort(b+l[pl],b+1+r[pl]);//重新排序
for(int i=l[pr];i<=R;i++){//右侧
a[i]+=C;
}
for(int i=l[pr];i<=r[pr];i++){
b[i]=a[i];
}
sort(b+l[pr],b+r[pr]+1);
for(int i=pos[L]+1;i<=pos[R]-1;i++){//中间完整部分
vis[i]+=C;
}
}
}
int ans(int L,int R,int C){
int pl=pos[L];//左端点所在的块数
int pr=pos[R];//右端点所在的块数
int cnt=0;
if(pl==pr){//两端点相等时的情况
for(int i=L;i<=R;i++){
if(a[i]+vis[pl]<C)
cnt++;
}
return cnt;
}
for(int i=L;i<=r[pl];i++){暴力查询左侧不完整的块
if(a[i]+vis[pl]<C)
cnt++;
}
for(int i=l[pr];i<=R;i++){暴力查询右侧不完整的块
if(a[i]+vis[pr]<C)
cnt++;
}
//二分查找
for(int i=pos[L]+1;i<=pos[R]-1;i++){
cnt+=lower_bound(b+l[i],b+r[i]+1,C-vis[i])-b-l[i];
}
return cnt;
}
int main() {
cin>>n;
int len=sqrt(n);//分成的每个块的大小,一般长度都为sqrt(n)
int num=ceil(n*1.0/len);//大概有多少块
for(int i=1; i<=num; i++) {
l[i]=(i-1)*len+1;
r[i]=i*len;
}
r[num]=n;
for(int i=1; i<=n; i++) {
cin>>a[i];
b[i]=a[i];
pos[i]=(i-1)/len+1;//表示第i个数字在第几块里
}
for(int i=1; i<=num; i++) {
sort(b+l[i],b+r[i]+1);//对每个块进行排序
}
for(int i=1; i<=n; i++) {
cin>>opt>>ll>>rr>>c;
if(opt==0) {
f(ll,rr,c);
} else {
cout<<ans(ll,rr,c*c)<<endl;
}
}
return 0;
}
C . 数列分块入门 3
思路
与数列分块入门 2题目思路大致相同。
-
更新操作对【L,R】内元素+c,查询操作对【L,R】查找小于c的最大元素。
-
根据分块的思想,对于不完整的块用暴力的方法进行记录,对每一块进行排序,再用二分查找出最大的小于c的数。
注
注意数据类型的变化
Code:
/*
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =1e6+7;
ll n;
ll a[maxn];
ll L[maxn],R[maxn];
ll pos[maxn];
ll b[maxn];
ll vis[maxn];
ll len;
void f(int l,int r,int c) {
ll pl=pos[l];
ll pr=pos[r];
if(pl==pr) {
for(int i=l; i<=r; i++)
a[i]+=c;
for(int i=L[pl]; i<=R[pl]; i++)
b[i]=a[i];
sort(b+L[pl],b+1+R[pl]);
} else {
for(int i=l; i<=R[pl]; i++)
a[i]+=c;
for(int i=L[pl]; i<=R[pl]; i++)
b[i]=a[i];
sort(b+L[pl],b+1+R[pl]);
for(int i=L[pr]; i<=r; i++)
a[i]+=c;
for(int i=L[pr]; i<=R[pr]; i++)
b[i]=a[i];
sort(b+L[pr],b+1+R[pr]);
for(int i=pos[l]+1; i<=pos[r]-1; i++)
vis[i]+=c;
}
}
ll query(int l,int r,int c) {
int ans=-1;
ll pl=pos[l];
ll pr=pos[r];
ll t;
if(pl==pr) {
for(int i=l; i<=r; i++) {
t=a[i]+vis[pl];
if(t>ans && t<c)
ans=t;
}
} else {
for(int i=l; i<=R[pl]; i++) {
t=a[i]+vis[pl];
if(t>ans && t<c)
ans=t;
}
for(int i=L[pr]; i<=r; i++) {
t=a[i]+vis[pr];
if(t>ans && t<c)
ans=t;
}
for(int i=pos[l]+1; i<=pos[r]-1; i++) {
int pos=lower_bound(b+L[i],b+R[i]+1,c-vis[i])-b-1;
t=b[pos]+vis[i];
if(t>ans && t<c)
ans=t;
}
}
return ans;
}
int main() {
cin>>n;
len=sqrt(n);
ll num=ceil(n*1.0/len);//大概有多少块
for(int i=1; i<=n; i++) {
cin>>a[i];
b[i]=a[i];
}
for(int i=1; i<=num; i++) {
L[i]=(i-1)*len+1;
R[i]=i*len;
sort(b+L[i],b+1+R[i]);
vis[i]=0;
for(int j=L[i]; j<=R[i]; j++)
pos[j]=i;
}
while(n--) {
ll opt,x,y,w;
cin>>opt>>x>>y>>w;
if(opt==0)
f(x,y,w);
else
cout<<query(x,y,w)<<endl;
}
return 0;
}
D . 数列分块入门 4
思路
求区间和,建立一个sum数组储存每一块的总和
区间加法+区间求和
具体实现见代码
注
注意题目的数据类型,long long
Code:
/*
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int>PII;
const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =1e6+7;
int n;
int l[maxn];//第i块左区间端点
int r[maxn];//第i块右区间端点
int pos[maxn];//第i个元素位于第几块
ll a[maxn],sum[maxn];
int opt,lll,rr,c;
ll vis[maxn];
int len;
ll cnt;
void f(int L,int R,int C) {
int pl=pos[L];//左端点所在的块数
int pr=pos[R];//右端点所在的块数
if(pl==pr) {
for(int i=L; i<=R; i++) {
a[i]+=C;
sum[pl]+=C;
}
} else {
for(int i=L; i<=r[pl]; i++) { //修改左侧不完整的块
a[i]+=C;
sum[pl]+=C;
}
for(int i=l[pr]; i<=R; i++) { //右侧
a[i]+=C;
sum[pr]+=C;
}
for(int i=pos[L]+1; i<=pos[R]-1; i++) { //中间完整部分
vis[i]+=C;
}
}
}
ll ans(int L,int R,int C) {
int pl=pos[L];//左端点所在的块数
int pr=pos[R];//右端点所在的块数
cnt=0;
if(pl==pr) { //两端点相等时的情况
for(int i=L; i<=R; i++) {
cnt=(cnt+a[i]+vis[pl])%C;
}
return cnt%C;
}
for(int i=L; i<=r[pl]; i++) {//左端
cnt=(cnt+a[i]+vis[pl])%C;
}
for(int i=l[pr]; i<=R; i++) {//右端
cnt=(cnt+a[i]+vis[pr])%C;
}
for(int i=pos[L]+1; i<=pos[R]-1; i++) {//中间的进行二分查找
cnt=(cnt+sum[i]+vis[i]*len);
}
return cnt%C;
}
int main() {
cin>>n;
len=sqrt(n);//分成的每个块的大小,一般长度都为sqrt(n)
int num=ceil(n*1.0/len);//大概有多少块
for(int i=1; i<=num; i++) {
l[i]=(i-1)*len+1;
r[i]=i*len;
}
r[num]=n;
for(int i=1; i<=n; i++) {
cin>>a[i];
pos[i]=(i-1)/len+1;//表示第i个数字在第几块里
sum[pos[i]]+=a[i];
}
for(int i=1; i<=n; i++) {
cin>>opt>>lll>>rr>>c;
if(opt==0) {
f(lll,rr,c);
} else {
cout<<ans(lll,rr,c+1)<<endl;
}
}
return 0;
}
E . 数列分块入门 5
思路
区间开方+区间求和
-
用vis数组记录当前的块是否都是1,sum来记录这一块的和。修改的时候,如果是1的话,就不用再更新;如果不是,暴力一次,全开方,并对sum进行更新,再判断vis数组是否都是1,对v进行更新。
-
对于不完整块,进行暴力。将不完整块与完整块分开讨论。
Code:
/*
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod=998244353;
const int maxn =1e6+7;
int a[maxn];
int pos[maxn];
int sum[maxn];
int vis[maxn];
int n;
int len;
void f(int l,int r) {
int pl=pos[l];
int pr=pos[r];
if(pl==pr) {
for(int i=l; i<=r; i++) {
sum[pl]-=a[i];
a[i]=sqrt(a[i]);
sum[pl]+=a[i];
}
return ;
}
for(int i=l; pos[i]==pl; i++) {//修改左端不完整块 更新sum值
sum[pl]-=a[i];
a[i]=sqrt(a[i]);
sum[pl]+=a[i];
}
for(int i=r; pos[i]==pr; i--) {//修改右端不完整块 更新sum值
sum[pr]-=a[i];
a[i]=sqrt(a[i]);
sum[pr]+=a[i];
}
for(int i=pl+1; i<=pr-1; i++) {
if(vis[i]==0) {//作为标记
vis[i]=1;
for(int j=(i-1)*len+1; pos[j]==i; j++) {
sum[i]-=a[j];
a[j]=sqrt(a[j]);
sum[i]+=a[j];
if(a[j]>1)
vis[i]=0;
}
}
}
}
int k(int l,int r) {
int ans=0;
int pl=pos[l];
int pr=pos[r];
if(pl==pr) {
for(int i=l; i<=r; i++) {
ans+=a[i];
}
return ans;
}
for(int i=l; pos[i]==pl; i++)
ans+=a[i];
for(int i=r; pos[i]==pr; i--)
ans+=a[i];
for(int i=pl+1; i<=pr-1; i++)
ans+=sum[i];
return ans;
}
int main() {
cin>>n;
len=sqrt(n);
for(int i=1; i<=n; i++) {
cin>>a[i];
pos[i]=(i-1)/len+1;
sum[pos[i]]+=a[i];
}
for(int i=1; i<=n; i++) {
int opt,l,r,c;
cin>>opt>>l>>r>>c;
if(opt==0)
f(l,r);
else
cout<<k(l,r)<<endl;
}
return 0;
}
B . 数列分块入门 6
思路
原链接
前几个题目都是用数组把所有数据都存放在一起。
此题用vector分别存放每个块的数据
当块内数据较大的时候,进行重新分块。
优点:耗时少。
Code:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<stack>
#include<map>
#include<string>
#include<algorithm>
#include<sstream>
#include<memory>
#include<utility>
#include<functional>
#include<iterator>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxn=1e5+7;
int pos[maxn];
int vis[maxn];//每一块的长度
int a[maxn*2];//用于重构的时候临时存放所有数据,因为有插入,所以数组扩大两倍
int len,num;
int n;
int all;//数据总个数
vector<int>b[maxn];//b[i][j]表示元素在第i块中的第j个位置
void mer() {//分块
len=sqrt(n);//块的长度
num=ceil(n*1.0/len);//分块后新的块数
for(int i=1; i<=n; i++) {//第i个元素在第几块里
pos[i]=(i-1)/len+1;
b[pos[i]].push_back(a[i]);
vis[pos[i]]++;
}
}
void div() {//进行重新分块
int cnt=1;
for(int i=1; i<=num; i++) {//将已有的元素存储回a[i]
int len=b[i].size();
for(int j=0; j<len; j++) {
a[cnt++]=b[i][j];
}
b[i].clear();
vis[i]=0;
}
}
void get(int l,int r) {
int cnt=1;
while(l>vis[cnt]) {
l-=vis[cnt];
cnt++;
}
b[cnt].insert(b[cnt].begin()+l-1,r);
vis[cnt]++;
all++;
if(vis[cnt]>2*len) {//插入过多的话,进行重新分块
div();
mer();
}
}
void ins(int r) {
int cnt=1;
while(r>vis[cnt]) {
r-=vis[cnt];
cnt++;
}
cout<<b[cnt][r-1]<<endl;
}
int main() {
cin>>n;
for(int i=1; i<=n; i++) {
cin>>a[i];
}
mer();
for(int i=1; i<=n; i++) {
int opt,l,r,c;
cin>>opt>>l>>r>>c;
if(opt==0)
get(l,r);
else
ins(r);
}
return 0;
}
G . 数列分块入门 7
思路
用两个数组的、分别对加法和乘法进行标记。
不完整的块:
a[i] = a[i] × mult[b[i]] + add[b[i]] ;
完整的块:
- 乘法:( a[i] × mult[b[i]] + add[b[i]] ) × c = a[i] × (mult[b[i]] × c) + (add[b[i]] × c) ,将mult和add都乘以c.
- 加法:( a[i] × mult[b[i]] + add[b[i]] ) + c = a[i] × mult[b[i]] + (add[b[i]] + c),将add加上c。
Code:
/*
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod = 1e4 + 7;
const int maxn = 1e5 + 10;
int a[maxn];
int add[maxn];
int mult[maxn];
int pos[maxn];
int n;
int len;
void init(int t) {
for(int i = (t - 1) * len + 1; i <= t * len; i++) {
a[i]=(a[i]*mult[t])%mod;
a[i]=(a[i]+add[t])%mod;
}
mult[t] = 1;
add[t] = 0;
}
void ad(int l, int r, int c) {
init(pos[l]);
for(int i = l; i <= r && i <= pos[l] * len; i++)
a[i] = (a[i] + c) % mod;
if(pos[l] != pos[r]) {
init(pos[r]);
for(int i = (pos[r] - 1) * len + 1; i <= r; i++)
a[i] = (a[i] + c) % mod;
}
for(int i = pos[l] + 1; i <= pos[r] - 1; i++)
add[i] = (add[i] + c) % mod;
}
void mul(int l, int r, int c) {
init(pos[l]);
for(int i = l; i <= r && i <= pos[l] * len; i++)
a[i] = (a[i] * c) % mod;
if(pos[l] != pos[r]) {
init(pos[r]);
for(int i = (pos[r] - 1) * len + 1; i <= r; i++)
a[i] = (a[i] * c) % mod;
}
for(int i = pos[l] + 1; i <= pos[r] - 1; i++)
mult[i] = (mult[i] * c) % mod, add[i] = (add[i] * c) % mod;
}
int qush(int t) {
return (((a[t] * mult[pos[t]])) % mod + add[pos[t]]) % mod;
}
int main() {
cin>>n;
len = sqrt(n);
for(int i = 1; i <= n; i++) {
cin>>a[i];
a[i]=a[i]%mod;
pos[i] = (i - 1) / len+1;
mult[i] = 1;
}
for(int i = 1; i <= n; i++) {
int op , l , r , x ;
cin>>op>>l>>r>>x;
if(op==0) {
ad(l, r, x);
} else if(op==1) {
mul(l, r, x);
} else {
cout<< qush(r)<<endl;
}
}
return 0;
}
H . 数列分块入门 8
思路
用一个vis数组表示第i块是否都为一个数
b数组表示第i块变成的数
Code:
/*
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <cstdio>
#include <string>
#include <deque>
#include <stack>
#include <set>
#include <vector>
#include <bits/stdc++.h>
#include <map>
#include <cstring>
#include <iomanip>
#define ll long long
using namespace std;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
const ll INFll = 0x3f3f3f3f3f3f3f3fLL;
const int INF= 0x7ffffff;
const int mod = 1e4 + 7;
const int maxn = 1e5 + 10;
int n;
int pos[maxn];
int a[maxn],b[maxn];
int L[maxn],R[maxn];
int vis[maxn];
int len;
int l,r,c;
void f(int x) {
if(vis[x]==0)
return ;
for(int i=L[x]; i<=R[x]; i++)
a[i]=b[x];
vis[x]=0;
b[x]=-1;
}
int ans(int l,int r,int c) {
int cnt=0;
f(pos[l]);
for(int i=l; i<=min(R[pos[l]],r); i++)
if(a[i]==c)
cnt++;
else
a[i]=c;
if(pos[l]!=pos[r]) {
f(pos[r]);
for(int i=L[pos[r]]; i<=r; i++)
if(a[i]==c)
cnt++;
else
a[i]=c;
}
for(int i=pos[l]+1; i<=pos[r]-1; i++)
if(vis[i]) {
if(b[i]==c)
cnt+=len;
else
b[i]=c;
} else {
for(int j=L[i]; j<=R[i]; j++) {
if(a[j]==c)
cnt++;
}
vis[i]=1;
b[i]=c;
}
return cnt;
}
int main() {
cin>>n;
len=sqrt(n);
for(int i=1; i<=n; i++)
cin>>a[i];
for(int i=1; i<=n; i++) {
pos[i]=(i-1)/len+1;
if(L[pos[i]]==0)
L[pos[i]]=i;
R[pos[i]]=i;
}
for(int i=1; i<=n; i++) {
cin>>l>>r>>c;
cout<<ans(l,r,c)<<endl;
}
return 0;
}