[3月集训]D1T1

这里写图片描述
这里写图片描述

sol:

早上被吊锤,还以为发现了性质写了个70分,出来10分
考虑这题用高消做,给每个正方形右下角的角度数弄成90+x[i][j]的形式。
那么显然可以得到4个相邻方格的方程。然后把上一个方程代到下一个方程里,不停的做,就可以得到若干个x1,1-xi,1-x1,i+xi,j=0的方程。这样的话其实我们只要求出一行一列的话,就可以得到所有的解了。显然如果解唯一的话就是S否则为U。那我们发现如果xi,j是‘.’的话,这个方程可以舍弃掉,他并不对我们求解第一行第一列有用。如果x1,1是x的话,那么x1,1=0.这个方程就变成了xi,1=x1,i的形式,这个关系就非常好了,我们考虑如果x1,1不是0怎么办,我们发现完全可以把1,1挪一挪,用其他的x来当做中心点,且显然问题等价。
那么我们的问题就变成求解某行某列,然后给出若干xk,i=xj,k的关系,考虑这个东西只要知道一者另一者就知道了,所以把这种关系当成边,求生成树然后看能否包含所有点即可。那么点数就是n+m的。
但是这个问题是动态的,也就是说边是有加入删除的,那么一条边就有若干个出现时间的区间,那么我们枚举当前的时间,然后边按照l,r排序,用lct维护一下双瓶颈生成树即可。那么我们关心的有2个地方,当前树是否包含n+m个点,当前树边最小的l是否>当前时间。前面的我用lct维护子树信息或者用堆做都可以。后面的用双堆维护即可。
但是一开始的边数有3000^2,这就很gg了。但是考虑到会变化的边只有q条,我们把不变的边用并查集并一下,然后把一个集合的点提前连边即可。

lct真的有点小麻烦,别人写线段树分治好像特别快。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;

int n,m,p;
const int N=1e6;
int lc[N],rc[N],ans[N],fa[N],val[N],real[N];
int sum1[N],sum2[N];
int father[N];

int en;
struct ed
{
    int x,y,l,r;
    friend inline bool operator <(const ed &a,const ed &b)
    {
        return a.l<b.l||a.l==b.l&&a.r<b.r;
    }
}edge[N];
inline int find(int x)
{
    return father[x]==x?x:father[x]=find(father[x]);
}
inline void updata(int x)
{
    sum1[x]=sum1[lc[x]]+sum1[rc[x]]+sum2[x]+real[x];
    if(val[ans[lc[x]]]<val[ans[rc[x]]]) ans[x]=ans[lc[x]];
    else ans[x]=ans[rc[x]];
    if(val[ans[x]]>val[x]) ans[x]=x;
}
inline void rotate(int x)
{
    int y=fa[x],z=fa[y];
    int b=lc[y]==x?rc[x]:lc[x];
    if(b) fa[b]=y;
    fa[x]=z;fa[y]=x;
    if(z)
    {
        if(lc[z]==y) lc[z]=x;
        if(rc[z]==y) rc[z]=x;
    }
    if(lc[y]==x) rc[x]=y,lc[y]=b;
    else lc[x]=y,rc[y]=b;
    updata(y); 
}
inline bool is_root(int x)
{
    return lc[fa[x]]!=x&&rc[fa[x]]!=x; 
}
bool rev[N];
inline void tag_rev(int x)
{
    rev[x]=!rev[x];
    swap(lc[x],rc[x]);
}
inline void tag_down(int x)
{
    if(rev[x])
    {
        tag_rev(lc[x]);
        tag_rev(rc[x]);
        rev[x]=0;
    }
}
int sta[N];
inline void splay(int x)
{
    sta[sta[0]=1]=x;
    for(int y=x;!is_root(y);y=fa[y]) sta[++sta[0]]=fa[y];
    while(sta[0]) tag_down(sta[sta[0]--]);
    while(!is_root(x))
    {
        if(!is_root(fa[x]))
        {
            if((lc[fa[x]]==x)==(lc[fa[fa[x]]]==fa[x])) rotate(fa[x]);
            else rotate(x);
        }
        rotate(x);
    }
    updata(x);
}
inline void access(int q)
{
    for(int p=0;q;p=q,q=fa[q])
    {
        splay(q);
        sum2[q]+=sum1[rc[q]];
        rc[q]=p;
        sum2[q]-=sum1[rc[q]];
        updata(q);
    }
}
inline void make_root(int x)
{
    access(x);
    splay(x);
    tag_rev(x);
}
inline void cut(int x,int y)
{
    make_root(x);
    access(y);
    splay(x);
    rc[x]=fa[y]=0;
    updata(x);
}
inline void link(int x,int y)
{
    make_root(x);
    access(y);
    splay(y);
    fa[x]=y;
    sum2[y]+=sum1[x];
    updata(y);
}
const int M=3100;
bool vis[M][M];
inline void unit(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy) father[fy]=fx;
}
inline int find_rt(int x)
{
    access(x);
    splay(x);
    tag_down(x);
    while(lc[x])
    {
        x=lc[x];
        tag_down(x); 
    }
    return x;
}
vector<int> q[M][M];
char sr[M][M];
int point;
struct cc
{
    int x,y;
}v[N];
struct P
{
    int x;
    friend inline bool operator <(const P &a,const P &b)
    {
        return a.x>b.x;
    }
};
priority_queue<P> aa,ab;
inline int top()
{
    while(!aa.empty()&&!ab.empty()&&aa.top().x==ab.top().x) aa.pop(),ab.pop();
    return aa.top().x; 
}
int main()
{
//  freopen("a.in","r",stdin);
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",sr[i]+1);
        for(int j=1;j<=m;++j)
        if(sr[i][j]=='x') q[i][j].push_back(0);
    }
    for(int i=1;i<=p;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        vis[x][y]=1;
        q[x][y].push_back(i); 
    }
    for(int i=0;i<=n+m;++i) real[i]=1,father[i]=i,val[i]=1e9;
    real[n+1]=real[0]=0;
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    if(!vis[i][j])
    {
        if(sr[i][j]=='x')
        unit(i,j+n);
    }
    else if(q[i][j].size())
    {
        if(q[i][j].size()&1) q[i][j].push_back(p+1);
        for(int k=0;k<q[i][j].size();k+=2)
        edge[++en]=(ed){i,j+n,q[i][j][k],q[i][j][k+1]-1};
    }
    for(int i=1;i<=n+m;++i)
    {
        int t=find(i);
        if(t!=i)
        edge[++en]=(ed){i,t,0,p};
    }
    sort(edge+1,edge+1+en);
    point=n+m;
    int i=1;
    for(int j=0;j<=p;++j)
    {
        for(;edge[i].l<=j&&i<=en;++i)
        {
            int x,y,z;
            x=edge[i].x;
            y=edge[i].y;
            z=edge[i].r;
            if(find_rt(x)!=find_rt(y))
            {
                val[++point]=z;
                updata(point);
                v[point].x=x;
                v[point].y=y;
                link(x,point);
                link(point,y);
                aa.push((P){z});
            }
            else
            {
                make_root(x);
                access(y);
                splay(x);
                if(val[ans[x]]<z)
                {
                    int t=ans[x];
                    cut(v[t].x,t);
                    cut(v[t].y,t);
                    val[++point]=z;
                    updata(point);
                    v[point].x=x;
                    v[point].y=y;
                    link(x,point);
                    link(point,y);
                    ab.push((P){val[t]});
                    aa.push((P){z});
                }
            }
        }
        make_root(1);
        splay(1);
        if(sum1[1]!=n+m-1) printf("U\n");
        else
        {
            if(top()<j) printf("U\n");
            else printf("S\n");
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值