zoukankan      html  css  js  c++  java
  • usaco 2017 US Open

    来自FallDream的博客。未经允许,请勿转载,谢谢。

    ----------------------------------------------------

    A:Modern Art

    给定一个n*n的网格,有n*n个颜色,每种颜色按一定顺序覆盖了一个矩形。给定末状态,求有几种颜色可能是第一个填上去的。n<=1000

    题解:二维查分+前缀和起来,然后就可以快速求得每个点被覆盖了多少次啦。复杂度n^2

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n,ans=0;
    int x1[1000005],x2[1000005],y1[1000005],y2[1000005];
    int f[1005][1005],s[1005][1005];
    bool b[1000005];
    
    int main()
    {
        n=read();
        for(int i=1;i<=n*n;i++)x2[i]=y2[i]=0,x1[i]=y1[i]=1004;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)    
            {
                int x=read();s[i][j]=x;if(!x)continue;
                if(!b[x])ans++,b[x]=1;x1[x]=min(x1[x],i);x2[x]=max(x2[x],i);
                y1[x]=min(y1[x],j);y2[x]=max(y2[x],j);
            }
        if(ans==1)return 0*printf("%d",n*n-1);ans=n*n;
        for(int i=1;i<=n*n;i++)if(b[i])
        {
            f[x1[i]][y1[i]]++;f[++x2[i]][y1[i]]--;
            f[x1[i]][++y2[i]]--;f[x2[i]][y2[i]]++;    
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)f[i][j]+=f[i-1][j];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)f[i][j]+=f[i][j-1];
    
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(s[i][j]&&b[s[i][j]])
                {
                    int x=s[i][j];
                    if(f[i][j]>1) b[x]=0,ans--;
                }
        cout<<ans;
        return 0;
    }

    B.Switch Grass

    给定一个n个点m条边的图,每个点有一个颜色,边有边权。q个操作,每次修改一个点的颜色,询问连接的两个点颜色不同的权值最小的边的权值。n,m,q<=200000

    先提供一种玄学做法,最坏n^2,但是usaco数据弱,也A了。

    我们先把边按照权值排序,然后用fail[i]表示前i条边都不合法的情况下,下一条颜色可能不同的边是哪条。这个可以通过并查集处理出来。然后记录每个点最先出现的边的编号。每次修改,看一下修改的点的编号是否比答案小,如果小,那条边一定成为答案。如果不小的话,看一下是否把现在的答案这条边改成了相同的颜色,如果相同就沿着fail指针找到第一个不同的点,复杂度最坏n^2,但是由于数据弱,并且很多操作是O(1)的,所以卡过了,最久的点只跑了1074ms。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define MAXN 200000
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n,m,k,q,ans;
    int fail[MAXN+5];
    int s[MAXN+5],col[MAXN+5],first[MAXN+5];
    struct edge{
        int to,from,w,num;
    }e[MAXN+5];
    bool cmp(edge x,edge y){return x.w<y.w;}
    int getfa(int x){return s[x]==x?x:s[x]=getfa(s[x]);}
    bool combine(int x,int y){x=getfa(x);y=getfa(y);if(x!=y)s[x]=y;}
    bool check(int x,int y){return getfa(x)==getfa(y);}
    
    int main()
    {
        freopen("grass.in","r",stdin);freopen("grass.out","w",stdout);
        n=read();m=read();k=read();q=read();
        for(register int i=1;i<=m;i++)
        {e[i].from=read();e[i].to=read();e[i].w=read();e[i].num=i;}
        sort(e+1,e+m+1,cmp);
        for(int i=1;i<=n;i++)s[i]=i;
        for(register int i=1,j=2;i<=m;i++)
        {
            combine(e[i].from,e[i].to);
            for(;j<=m&&check(e[j].from,e[j].to);)++j;    
            fail[i]=j;
        }memset(first,127,sizeof(first));
        for(register int i=1;i<=n;i++)col[i]=read();
        for(register int i=1;i<=m;i++){first[e[i].to]=min(first[e[i].to],i);first[e[i].from]=min(first[e[i].from],i);}
        for(register int i=1;i<=m;i++)
        if(col[e[i].from]!=col[e[i].to]){ans=i;break;}
        for(register int i=1;i<=q;i++)
        {
            int x=read(),y=read();col[x]=y;
            if(first[x]<ans)ans=first[x];
            else while(col[e[ans].from]==col[e[ans].to])ans=fail[ans];
            printf("%d
    ",e[ans].w);
        }
        return 0;
    }

    官方题解:我们可以发现对答案有贡献的边只会在最小生成树上,所以先跑出mst然后给他定一个根。然后我们对每一个点,对它儿子所有可能的颜色都建一个堆。 

    这样修改一个点只要从它父亲那里删掉一个点然后重新扔一个进去就可以了。这样每个点的答案就是和它颜色不同的堆的最小值,之后我们再对整体建一个堆,把每个

    堆的答案扔进去,就可以啦。

    我觉得堆有点难受,所以对每个节点建了个平衡树,还作死写了splay,之后再用线段树搞起来,差点T了.....复杂度nlogn 跑得比暴力还慢...

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define MAXN 1500000
    #define MAXL 200000
    #define N 262144
    #define INF 2000000000
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    int n,m,qq,cnt=0;
    int fa[MAXN+5],fat[MAXL+5],c[MAXN+5][2],s[MAXN+5],head[MAXL+5],ans[MAXN+5];
    int rt[MAXL+5],col[MAXN+5],num[MAXN+5],mx[MAXN+5],size[MAXN+5],id[MAXN+5],upx[MAXN+5];
    int T[N*2+5];
    struct edge{
        int from,to,w;
    }e[200005];
    struct edge_type_2{
        int next,to,w;
    }e2[400005];
    
    bool cmp(edge x,edge y){return x.w<y.w;}
    inline int getfa(int x){return !s[x]?x:s[x]=getfa(s[x]);}
    
    void add(edge x)
    {
        int f=x.from,t=x.to,w=x.w;
        e2[++cnt].next=head[f];e2[cnt].to=t;
        head[f]=cnt;e2[cnt].w=w;
        e2[++cnt].next=head[t];e2[cnt].to=f;
        head[t]=cnt;e2[cnt].w=w;
    }
    
    inline void mst()
    {
        int x,y;
        for(register int i=1;i<=m;i++)
        {
            x=getfa(e[i].from);y=getfa(e[i].to);
            if(x!=y)s[x]=y,add(e[i]);
        }
    }
    
    void renew(int x,int ad)
    {
       // cout<<"renew"<<x<<" "<<ad<<endl;
        T[x+=N]=ad;
        for(x>>=1;x;x>>=1)T[x]=min(T[x<<1],T[x<<1|1]);
    }
    
    inline void update(int x)
    {
        if(!x)return;
        int l=c[x][0],r=c[x][1];
        size[x]=size[l]+size[r]+1;
        mx[x]=min(mx[l],min(mx[r],num[x]));
    }
    
    void rotate(int x,int&k)
    {
        int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
        if(y==k)k=x;
        else c[z][c[z][1]==y]=x;
        fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
        c[y][l]=c[x][r];c[x][r]=y;
        update(y);update(x);
    }
    
    void splay(int x,int&k)
    {
      //  cout<<"splay"<<x<<endl;
        while(x!=k)
        {
            int y=fa[x],z=fa[y];
            if(y!=k)
            {
                if(c[z][1]==y^c[y][1]==x)rotate(x,k);
                else rotate(y,k);
            }
            rotate(x,k);
        }
    }
    
    void ins(int&x,int rk,int last,int w,int fid)
    {
      //  cout<<"ins"<<x<<" "<<rk<<" "<<last<<" "<<w<<endl;
        if(!x){x=++cnt;fa[x]=last;s[x]=rk;num[x]=mx[x]=w;size[x]=1;id[x]=fid;return;}
        if(s[x]==rk)ins(c[x][(fid>id[x])],rk,x,w,fid);
        else if(s[x]>rk)ins(c[x][0],rk,x,w,fid);
        else ins(c[x][1],rk,x,w,fid);
        update(x);
    }
    
    void dfs(int x,int f)
    {
        fat[x]=f;
        for(int i=head[x];i;i=e2[i].next)
        if(e2[i].to!=f)
        {upx[e2[i].to]=e2[i].w;ins(rt[x],col[e2[i].to],0,e2[i].w,e2[i].to);dfs(e2[i].to,x);}
    }
    
    int find(int x,int rk,int fid)
    {
        if(s[x]==rk)
        {if(id[x]==fid)return x;return find(c[x][(fid>id[x])],rk,fid);}
        if(s[x]>rk)return find(c[x][0],rk,fid);
        else return find(c[x][1],rk,fid);
    }
    
    int get(int x,int rk)
    {
       // cout<<"get"<<x<<" "<<rk<<endl;
        int l=c[x][0],r=c[x][1];
        if(size[l]+1==rk)return x;
        else if(size[l]+1<rk)return get(r,rk-size[l]-1);
        else return get(l,rk);
    }
    
    int del(int x)
    {
      //  cout<<"del"<<x<<endl;
        int y=fat[x];
        splay(find(rt[y],col[x],x),rt[y]);
        int w=num[rt[y]],rk=size[c[rt[y]][0]];
        splay(get(rt[y],rk),c[rt[y]][0]);
        int pre=c[rt[y]][0];
        c[pre][1]=c[rt[y]][1];
        fa[c[rt[y]][1]]=pre;rt[y]=pre;update(pre);
        return w;
    }
    
    int ask(int x,int num,int pos)
    {
     //   cout<<"ask"<<x<<" "<<num<<" "<<pos<<" "<<s[x]<<endl;
        if(!x)return 0;int q;
        if(s[x]==num)return (q=ask(c[x][pos],num,pos))>0?q:x;
        else if(s[x]<num)return ask(c[x][1],num,pos);
        else return ask(c[x][0],num,pos);
    }
    
    int query(int x)
    {
        int l=ask(rt[x],col[x],0),r=ask(rt[x],col[x],1);
        if(!l)return mx[rt[x]];
        splay(l,rt[x]);if(r==l)return min(mx[c[l][0]],mx[c[l][1]]);
        splay(r,c[l][1]);
        return min(mx[c[rt[x]][0]],mx[c[c[rt[x]][1]][1]]);
    }
    
    int main()
    {
        freopen("grass.in","r",stdin);
        freopen("grass.out","w",stdout);
        n=read();m=read();read();qq=read();mx[0]=INF;
        for(register int i=1;i<=m;i++)
            e[i].from=read(),e[i].to=read(),e[i].w=read();
        for(register int i=1;i<=n;++i)col[i]=read();
        sort(e+1,e+m+1,cmp);
        mst();cnt=0;
        for(register int i=1;i<=n;++i)ins(rt[i],-INF,0,INF,0),ins(rt[i],INF,0,INF,0);
        dfs(1,0);
        for(register int i=1;i<=2*N+1;++i)T[i]=INF;
        for(register int i=1;i<=n;++i)renew(i,ans[i]=query(i));
        int x,c;
        for(register int i=1;i<=qq;++i)
        {
            x=read(),c=read();
            if(col[x]!=c)
            {
                if(x!=1){ins(rt[fat[x]],c,0,del(x),x);
                    if(upx[x]<ans[fat[x]]||c==col[fat[x]])renew(fat[x],ans[fat[x]]=query(fat[x]));}
                col[x]=c;renew(x,ans[x]=query(x));
            }
            printf("%d
    ",T[1]);
        }
        return 0;
    }

     C沙比提不想做

  • 相关阅读:
    单臂路由
    C#同步、异步编程
    Grid控件
    使用WrapPanel和DockPanel
    使用StackPanel进行简单地布局
    WPF布局
    SQL update 多表连接方法
    创建一个自定义的Application类
    Application全局应用程序类
    XAMl使用其他命名空间中的类型及加载和编译
  • 原文地址:https://www.cnblogs.com/FallDream/p/usaco2017usopen.html
Copyright © 2011-2022 走看看