TC SRM568,奇妙的题目

这道题一开始的想法是瞎贪心一波,建张图出来,然后跑带花树,看看是否有完美匹配。
描述一下如何建图,首先贪心,如果连接一个已知点对的半圆方向能向上就向上,否则向下。
再引入一个概念,对于一个未确定点 P ,定义f(d,P)为d方向上覆盖了这个点的半圆集合。
考虑存在一个方向 dx 使得 f(dx,P1) 等于 f(dx,P2) 的点对 (P1,P2) 之间连一条边,跑下带花树,我的算法就结束啦。


下面是正解的开始。
定义 g(b) 为半圆b所包含的点集,可以证明前面的图存在完美匹配就等价与所有的 g(b) 大小都是偶数。
并且,前述的贪心建图,是不对的,应当枚举。
枚举完了之后,可以通过前缀和的思想将问题转化为二分图判定,复杂度最坏 O(2n/2n) 。理论上是不能过的,但是TC上8086ms硬给卡过去了,实际上可以用些特技来加速。
下面是蒟蒻的代码:

#include<cstring>
#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
const  int N=115;
class DisjointSemicircles{
    int f[N],n,i,j,o,l[N],r[N],cnt,a[N],id[N],st[2][N],w[2],rr[N],ll[N];
    static const int D=53;
    inline int gfa(int x){return f[x]==x?x:f[x]=gfa(f[x]);}
    inline void dif(int x,int y){//different
        f[gfa(x)]=gfa(y+D);f[gfa(x+D)]=gfa(y);
    }
    inline void equ(int x,int y){//equal
        f[gfa(x)]=gfa(y);f[gfa(x+D)]=gfa(y+D);
    }
    public:inline std::string getPossibility(std::vector<int>labels){
        n=labels.size();memset(l,-1,sizeof l);memset(r,-1,sizeof r);memset(id,-1,sizeof id);
        memset(ll,-1,sizeof ll);memset(rr,-1,sizeof rr);
        for(i=0;i<n;++i)if(~labels[i]){
            if(id[labels[i]]==-1)id[labels[i]]=cnt++;j=id[labels[i]];
            if(~l[j])rr[ll[i]=l[j]]=r[j]=i;
                else l[j]=i;
        }   
        if(!cnt)return "POSSIBLE";
        //for(i=0;i<n;++i)printf("%d ",ll[i]);putchar('\n');
        //for(i=0;i<n;++i)printf("%d ",rr[i]);putchar('\n');
        for(i=0;i<1<<cnt;++i){
            for(j=0;j<=n;++j)f[j]=j,f[j+D]=j+D;
            for(j=0,w[0]=w[1]=0;j<n;++j)if(~labels[j]){
                o=i>>id[labels[j]]&1;
                if(~rr[j]){
                    if(w[o] && st[o][w[o]]<rr[j])break;st[o][++w[o]]=rr[j];
                }else if(~ll[j])--w[o];
            }
            if(j<n)continue;
            for(j=0;j<cnt;++j)if(l[j]!=r[j]){
                if(i>>j&1){
                    dif(l[j],l[j]+1);
                    dif(r[j],r[j]+1);
                }else{
                    equ(l[j],l[j]+1);
                    equ(r[j],r[j]+1);
                }
                if(l[j]==r[j]-1)continue;
                if(i>>j&1){
                    equ(l[j]+1,r[j]);
                }else{
                    if((r[j]-l[j]-1)&1)dif(l[j]+1,r[j]);
                        else equ(l[j]+1,r[j]);
                }
            }
            for(j=0,equ(0,n);j<=n && gfa(j)!=gfa(j+D);++j);
            if(j>n)return /*printf("%d\n",i),*/"POSSIBLE";
        }
        return "IMPOSSIBLE";
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值