[atARC119D]Grid Repainting 3

将每一行和每一列分别作为一个点,当第$i$行第$j$列的格子为红色时,将第$i$行与第$j$列连边

此时,考虑选择第$i$行的红色格子并将第$i$行的格子全部改成白色:

关于这一操作的条件,即需要第$i$行有红色格子,从图中来看也即第$i$行对应的点度非0

关于这一条件的影响,即第$i$行的红色格子都没了,从图中来看也即删去第$i$行对应的点所有出边

根据上述分析,每一次操作即在图中选择一个度非0的点,并删除其所有出边,并且设最终选择了$x$个行对应的点和$y$个列对应的点,最大化$nm-(n-x)(m-y)$

考虑其中的一个连通块(连通块之间显然是独立的),我们可以仅保留这个连通块中的任意一个点,并选择该连通块中其余的点

具体来说,任取该连通块的一个生成树,并以需要保留的点为根,每一次不断选择一个叶子即可

另一方面,显然无论如何我们都不可能删除一个连通块中的所有点,因此这已经最优了

同时,我们仅关心每一个连通块中未选的点是行还是列,一个点的连通块显然不需要考虑,多个点的连通块必然同时含有行和列的点

由此,求出所有连通块(不考虑一个点的连通块)的数量$k$以及$x-1$和$y-1$之和$X$和$Y$(其中$x$和$y$为该连通块中行和列对应的点数量),问题即求$f(x)=nm-(n-X-x)(m-Y-k+x)$在$x\in [0,k]$的最大值,利用二次函数简单讨论即可

方案根据上面的分析也不难构造,时间复杂度为$o(nm)$,可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 2505
 4 struct Edge{
 5     int nex,to;
 6 }edge[N*N*2];
 7 struct Op{
 8     int type,x,y;
 9 };
10 vector<int>v[N<<1];
11 vector<Op>ans;
12 int E,n,m,x,y,scc,head[N<<1],vis[N<<1];
13 char s[N][N];
14 void add(int x,int y){
15     edge[E].nex=head[x];
16     edge[E].to=y;
17     head[x]=E++;
18 }
19 void dfs(int k){
20     if (vis[k])return;
21     vis[k]=1;
22     v[scc].push_back(k);
23     for(int i=head[k];i!=-1;i=edge[i].nex)dfs(edge[i].to);
24 }
25 void dfs(int k,int fa){
26     if (vis[k])return;
27     vis[k]=1;
28     for(int i=head[k];i!=-1;i=edge[i].nex)dfs(edge[i].to,k);
29     if (fa>=0){
30         if (k<n)ans.push_back(Op{0,k,fa-n});
31         else ans.push_back(Op{1,fa,k-n});
32     }
33 }
34 void write(){
35     printf("%d\n",ans.size());
36     for(int i=0;i<ans.size();i++){
37         ans[i].x++,ans[i].y++;
38         if (ans[i].type==0)printf("X %d %d\n",ans[i].x,ans[i].y);
39         else printf("Y %d %d\n",ans[i].x,ans[i].y);
40     }
41 }
42 int main(){
43     scanf("%d%d",&n,&m);
44     memset(head,-1,sizeof(head));
45     for(int i=0;i<n;i++){
46         scanf("%s",s[i]);
47         for(int j=0;j<m;j++)
48             if (s[i][j]=='R'){
49                 add(i,j+n);
50                 add(j+n,i);
51             }
52     }
53     for(int i=0;i<n+m;i++)
54         if (!vis[i]){
55             v[++scc].clear();
56             dfs(i);
57             if (v[scc].size()==1)scc--;
58             else{
59                 x--,y--;
60                 for(int j=0;j<v[scc].size();j++){
61                     if (v[scc][j]<n)x++;
62                     else y++;
63                 }
64             }
65         }
66     memset(vis,0,sizeof(vis));
67     if (m-n+x-y<0){
68         for(int i=1;i<=scc;i++)
69             for(int j=0;j<v[i].size();j++)
70                 if (v[i][j]<n){
71                     dfs(v[i][j],-1);
72                     break;
73                 }
74         write();
75     }
76     else{
77         for(int i=1;i<=scc;i++)
78             for(int j=0;j<v[i].size();j++)
79                 if (v[i][j]>=n){
80                     dfs(v[i][j],-1);
81                     break;
82                 }
83         write();
84     }
85 }
View Code

 

posted @ 2021-06-01 08:32  PYWBKTDA  阅读(76)  评论(0编辑  收藏  举报