Gym - 101550A-并查集&离线-Artwork

http://codeforces.com/gym/101550/attachments
如图所示。
这里写图片描述
每次染色,然后问你联通块数有多少。
直接看图就行。
思路:比赛的时候想出来了,但是时间不够了,写了一个开头,本来是写对了,但是我看错了坐标,感觉很麻烦就没写。。
离线处理,然后先记录最后一个的联通块数(有一个空块就+1,联通一次就-1.这样就能得到联通块数)然后依次减去以前的操作,再次统计。
错的地方:① 我把他们都变成了一维表,因为我不太会二维并查集并且我感觉那样很慢。。但是有一个地方要注意,下面会画图说。
② 这道题的坐标很魔性。。
这里写图片描述
绿色这一排,不可以-1,红色这一排不可以+1,(摔啊,直接用一个二维数组存,然后并查的时候用一维不就好了么。用二维的方向坐标就没这么多事了qwq)

#include <bits/stdc++.h>
using namespace std;
/*  暴力维护。
离线处理。
*/
const int maxn=1e6+5000;
struct Node{
  int x,y;
  int x2,y2;
  Node(int a,int b,int c,int d){
      x=a;
      y=b;
      x2=c;
      y2=d;
  }
};
stack<Node>q;
stack<int>pp;
int num[maxn];
int m,n,q1;
int a,b,c,d;
int fa[maxn];
int cost[4];
int  find2(int x){
     if(x==fa[x]) return x;
     return fa[x]=find2(fa[x]);
}
int ans;
bool merge2(int x,int y){
    if(x<0||y<0||x>=m*n||y>=m*n) return false;
    int a=find2(x);
    int b=find2(y);
    if(a==b) return false;
    fa[a]=b;
    //cout<<"change"<<a<<" "<<b<<"????????"<<x<<" "<<y<<endl;
    ans--;
    return true;
}
int main()
{    scanf("%d%d%d",&m,&n,&q1);
     cost[0]=1;
     cost[1]=-1;
     cost[2]=n;
     cost[3]=-n;
     for(int i=0;i<m*n;i++){
        fa[i]=i;
     }
     for(int i=0;i<q1;i++){
          scanf("%d%d%d%d",&a,&b,&c,&d);
          a--,b--,c--,d--;
          swap(a,b);swap(c,d);

          if(a==c){
             if(b>d) swap(b,d);
             for(int j=b;j<=d;j++)
                 num[j*n+a]++;
          }
          else if(b==d){
              if(a>c) swap(a,c);
              for(int j=a;j<=c;j++){
                 num[b*n+j]++;
              }
          }
           q.push(Node(a,b,c,d));
     }
     ans=0;
      for(int i=0;i<m*n;i++){
           if(!num[i]){
                //cout<<num[i]<<" "<<i<<endl;
                ans++;
          for(int j=0;j<4;j++){
              if(j==0&&(i%n==(n-1))) continue;
              if(j==1&&i%n==0) continue;
              if(!num[i]&&!num[i+cost[j]])
                 { merge2(i,i+cost[j]);
                 }
          }
          }
      }
      //cout<<ans<<"!!"<<endl;
      pp.push(ans);
     while(!q.empty()){
          Node u=q.top();
          q.pop();
          a=u.x;
          b=u.y;
          c=u.x2;
          d=u.y2;
          //cout<<a<<"????"<<b<<c<<d<<endl;
           if(a==c){
             if(b>d) swap(b,d);
             for(int j=b;j<=d;j++){
                 num[j*n+a]--;
                 int t=j*n+a;
                 if(!num[t]) {ans++;
                 //cout<<t<<"!!!"<<endl;
              for(int j=0;j<4;j++){
                      if(j==0&&t%(n)==(n-1)) continue;
              if(j==1&&t%n==0) continue;
                 if(!num[t]&&!num[t+cost[j]]){
                     merge2(t,t+cost[j]);
                 }
              }
              }
              }
          }
          else if(b==d){
              if(a>c) swap(a,c);
              for(int j=a;j<=c;j++){
                 num[b*n+j]--;
                 int tt=b*n+j;
                 if(!num[tt]) ans++;
              for(int j=0;j<4;j++){
                      if(j==0&&tt%(n)==(n-1)) continue;
              if(j==1&&tt%n==0) continue;
                 if(!num[tt]&&!num[tt+cost[j]]){
                     merge2(tt,tt+cost[j]);
                 }
              }
              }
          }
      pp.push(ans);
     }
     pp.pop();
     while(!pp.empty()){
           printf("%d\n",pp.top());
           pp.pop();
     }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值