CF1500D Tiles for Bathroom (递推+大讨论)

题目大意:给你一个n*n的矩阵,现在问对于每个k\le n,求出所有k*k的子矩阵中,元素种类数不超过q的矩阵个数,n\le 1500, q\le 10

 

先考虑最暴力的做法:

对于每个格子,求出以它为子矩阵右下角时,左上角能到达的最远位置,时间O(n^{4})。再统计起来跑个后缀和

 

考虑可以$n^{3}$时的做法:

双指针对一条斜线上的格子进行维护,暴力进行指针移动时的更新

虽然双指针是一种优化递推,但其并没有利用q=10的性质

 

考虑直接递推

我们从左到右再从上到下依次处理每个点

对于每个格子维护一个vector,记录从它开始,不断向左上拓展时,第一次出现某个元素的相对位置信息

比如

1 2 3 ...

1 5 3 ...

1 2 3 ...

...

现在要处理(3,3)位置上的那个3

那么它相对于(3,3)的位置就是(3,3),中间的5相对位置是(2,2)。

5下面的2相对位置也是(2,2),因为如果想在(3,3)把这个2框进去,左上角一定会拓展到(2,2)

5上面的2相对位置是(1,1),但可惜在记录的时候我们只记录第一次出现的位置

我们最多只需要记录q+1个元素,第q+1个元素往右下一格就是答案

当前格子左,上,左上的三个相邻格子都是已知信息,取出来排序,再依次选第一次出现的位置

但左和上取出来时相对位置可能会改变

画图发现,这与元素在倒L形的左侧一列/上侧一行的出现情况有关,额外记录元素在这个倒L形左列/上行的出现情况,再进行大讨论即可转移

直接做会被卡空间,滚动数组就好

  1 int T,n,q; 
  2 int a[N1][N1],sum[N1][N1];
  3 struct node{
  4 int x,y,val; bool tp,le;
  5 };
  6 int cmp(node &s1,node &s2)
  7 {
  8     return s1.x+s1.y>s2.x+s2.y;
  9 }
 10 
 11 int ans[N1];
 12 int now,pst;
 13 vector<node>to[N1][N1];
 14 node se[50];
 15 int tot;
 16 
 17 void addnode(int x,int y,int nx,int ny)
 18 {
 19     int m; node k;
 20     if(x==nx-1&&y==ny-1)
 21     {
 22         m=to[pst][y].size(); 
 23         for(int i=0;i<m;i++)
 24         {
 25             k=to[pst][y][i]; se[tot++]=k;
 26             // se[tot++]=(node){k.x,k.y,k.val,k.tp.k.le};
 27         }
 28     }
 29     if(x==nx-1&&y==ny)
 30     {
 31         m=to[pst][y].size(); 
 32         for(int i=0;i<m;i++)
 33         {
 34             k=to[pst][y][i]; 
 35             if(x==k.x&&y==k.y){
 36                 if(y>1) k.y--; else continue;
 37                 k.le=0; k.tp=1;
 38             }else if(!k.tp){
 39                 k.x++; k.tp=(a[k.x][k.y]==k.val)?1:0; k.le=1;
 40             }else{
 41                 if(k.y>1) k.y--; else continue; // 后续与左侧取并!!
 42                 k.le=(a[k.x][k.y]==k.val)?1:0;
 43             }
 44             se[tot++]=k;
 45         }
 46     }
 47     if(x==nx&&y==ny-1)
 48     {
 49         m=to[now][y].size(); 
 50         for(int i=0;i<m;i++)
 51         {
 52             k=to[now][y][i]; 
 53             if(x==k.x&&y==k.y){
 54                 k.x--;
 55                 k.le=1; k.tp=0;
 56             }else if(!k.le){
 57                 k.y++; k.le=(a[k.x][k.y]==k.val)?1:0; k.tp=1;
 58             }else{
 59                 if(k.x>1) k.x--; else continue; // 后续与上侧取并!!
 60                 k.tp=(a[k.x][k.y]==k.val)?1:0;
 61             }
 62             se[tot++]=k;
 63         }
 64     }
 65 }
 66 void solve(int x,int y)
 67 {
 68     tot=0; se[tot++]=(node){x,y,a[x][y],1,1};
 69     addnode(x-1,y,x,y);
 70     if(y>1) addnode(x-1,y-1,x,y), addnode(x,y-1,x,y);
 71     sort(se,se+tot,cmp); node k; int fl;
 72     for(int i=0;i<tot;i++)
 73     {
 74         k=se[i]; fl=0;
 75         for(int j=0;j<to[now][y].size();j++)
 76         {
 77             if(to[now][y][j].val==k.val)
 78             {
 79                 if(to[now][y][j].x==k.x && to[now][y][j].y==k.y) 
 80                     to[now][y][j].tp|=k.tp, to[now][y][j].le|=k.le;
 81                 fl=1; break; 
 82             }
 83         }
 84         if(!fl)
 85         {
 86             to[now][y].push_back(k);
 87             if(to[now][y].size()==q+1) break;
 88         }
 89     }
 90     int m=to[now][y].size();
 91     // sum[x][y]=x-to[now][y][m-1].x+1;
 92     if(m==q+1) sum[x][y]=x-to[now][y][m-1].x;
 93     else sum[x][y]=min(x,y);
 94 }
 95 
 96 int ret[N1];
 97 int main()
 98 {
 99     // freopen("a.txt","r",stdin);
100     freopen("a.in","r",stdin);
101     // srand(time(NULL));
102     scanf("%d%d",&n,&q);
103     for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) read(a[i][j]);
104     now=1, pst=0;
105     for(int j=1;j<=n;j++)
106     {
107         to[0][j].push_back( (node){1,j,a[1][j],1,1} );
108         sum[1][j]=1;
109     }
110     for(int i=2;i<=n;i++)
111     {
112         for(int j=1;j<=n;j++)
113         {
114             to[now][j].clear();
115             solve(i,j); 
116         }
117         swap(now,pst);
118     }
119     for(int i=1;i<=n;i++)
120     {
121         for(int j=1;j<=n;j++)
122         {
123             ret[1]++; ret[sum[i][j]+1]--;
124             // printf("%d ",sum[i][j]);
125         }
126         // puts("");
127     }
128     for(int i=1;i<=n;i++) ret[i]=ret[i-1]+ret[i];
129     for(int i=1;i<=n;i++) printf("%d\n",ret[i]);
130     return 0;
131 }

 

posted @ 2021-03-17 19:15  guapisolo  阅读(84)  评论(0编辑  收藏  举报