题解:P11826 [TOIP2024] 奇巧方块
题目传送门
题目分析
一个基本定论是,要使每个
由于
由于所有黑色格子都在一条直线上,所以我们可以把题目先简化成:一条长为
设线段左端点的位置为
注意到,若
- 若
c_x+1-a \ge c_{y+1}-b ,则应将线段(a,b) 移至(a+(c_{y+1}-b),c_{y+1}) 。然后将y 加一。特别地,如果c_x+1-a = c_{y+1}-b ,则x 也要加一,因为在第y+1 号黑色格子移进线段内时,第x 号黑色格子也同时出除了线段。此时,该大移动内包含了c_{y+1}-b 个小移动,因此如果满足条件,情况数要加c_{y+1}-b 。 - 若
c_x+1-a < c_{y+1}-b ,则应将线段(a,b) 移至(c_{x}+1,b+(c_{x}+1-a)) 。然后将x 加一。此次大移动所包含的小移动数为c_x+1-a ,因此如果满足条件,情况数要加c_x+1-a 。
为了更直观地理解上面说的,下面给出一张图。
如图,所有标有字母的点为黑色格子。设点
这样,所有子矩阵中有黑色格子的情况数就计算完成了。设该数为
如果
答案还需加上含黑色格子且满足条件的子矩阵,注意到每一行该种矩阵数量一定,为
代码
//十分抱歉,由于本人失误,代码中m和n弄反了
#include<bits/stdc++.h>
#define N 1000001
using namespace std;
long long m,n,a,b,ans=0,x=0,now=0,t,k,r,c[N],jl=-1;
int main(){
cin>>n>>m>>t>>k>>r;
for(int i=1;i<=t;i++){
cin>>c[i];
if(c[i]<=k){
jl=i;
}
}
int ye=0;
if(c[t]!=m){
c[++t]=m;
ye=1;
}
a=1,b=k;
int na=1,nb=1+jl;
if(jl>=1){
nb--;
}
while(b<=m){
if(ye&&b==m){
if(jl==0){
break;
}
long long num=nb-na;
if(num%2!=k%2){
x++;
}
break;
}
if(b==m){
if(jl==0){
break;
}
long long num=nb-na+1;
if(num%2!=k%2){
x++;
}
break;
}
if(jl==0){
long long num=c[nb]-b;
if(k%2==1){
x+=num;
}
a+=(c[nb]-b);
b=c[nb];
jl=1;
}
else if(c[na]-a+1>=c[nb+1]-b){
long long len=c[nb+1]-b,num=nb-na+1;
if(num%2!=k%2){
x+=len;
}
int y=0;
if(c[na]-a+1==c[nb+1]-b){
y=1;
}
a+=(c[nb+1]-b);
b=c[nb+1];
if(y){
na++;
}
nb++;
jl=nb-na+1;
}
else{
long long len=c[na]-a+1,num=nb-na+1;
if(num%2!=k%2){
x+=len;
}
b+=(c[na]+1-a);
a=c[na]+1;
na++;
jl=nb-na+1;
if(!jl){
nb++;
}
}
}
now=(n-k+1)*(m-k+1);
long long up=max((long long)1,r-k+1),down=min(n,r+k-1);
now-=(m-k+1)*(down-up-k+2);
if(k%2==1){
ans=now;
}
ans+=x*(down-up-k+2);
cout<<ans;
return 0;
}