2012网赛杭州赛区

21 篇文章 0 订阅
13 篇文章 1 订阅

1002 arrest

有k个警察在0点按顺序遍历1到n去抓小偷, 这样构图时就要对编号小的连向编号大的, 之前要floyd处理。

我赛后的构图:对每个点的遍历有个限制是必须是1次, 由于是费用流, 可以用将该流置为-inf的方法,强制改变访问一次。

当时比赛时zzc的构图:跑k次网络流, 分别枚举1~k个警察情况下的最小费用, 也同样用了一个-inf的方法, 不过是加到了城市之间的连通边上,这样就麻烦了一些。


我的代码: source:0 , 0:1,  0'(sink):2*n+2,  i:i<<1, i' : 1<<1|1

        addedge(0, 1, K, 0);
        for (int i=1; i<=n; ++i)
            addedge(1, i<<1, 1, dist[0][i]), 
            addedge(i<<1, i<<1|1, 1, ness);
        
        for (int i=1; i<=n; ++i)
            for(int j=i+1; j<=n; ++j)
                addedge(i<<1|1, j<<1, 1, dist[i][j]);  
        
        for (int i=1; i<=n; ++i)
            addedge(i<<1|1, 2*n+2, 1, dist[i][0]);
        
        addedge(1, 2*n+2, K, 0);
        int ans=mcmf(0, 2*n+2)-n*ness;

zzc代码:

for(int kk = 1; kk <= K; ++kk)
        {
            // S->0, 0->1, 1->2, 1'->3, 0'(T), 2n+2
            g.build(2 * n + 3);
            g.addedge(0, 1, kk, 0);
            for(int i = 1; i <= n; ++i)
                g.addedge(2 * i, 2 * i + 1, 1, 0);
            for(int i = 1; i <= n; ++i)
            {
                g.addedge(1, 2 * i, 1, dist[0][i]);
                g.addedge(2 * i + 1, 2 * n + 2, 1, dist[i][0]);
            }
            for(int i = 1; i <= n; ++i)
            {
                for(int j = i + 1; j <= n; ++j)
                {
                    g.addedge(2 * i + 1, 2 * j, 1, dist[i][j] - inf);
                }
            }
            int ret = g.mincost(0, 2 * n + 2);
            ret += (n - kk) * inf;
            ans = std::min(ans, ret);
        }


1008 树状数组+离线+离散化也可以二分+划分树, 总之是道水题

using namespace std;
///*** for STL ***///
#define fst first
#define scd second
#define pb push_back
#define mp makepair
#define lb lower_bound
#define ub upper_bound
const int maxn=100000+123;
#define lowbit(x) ((x)&(-(x)))

int C[2*maxn];
int N;
int Query(int x){
    for (int res=0; ; res+=C[x], x-=lowbit(x))if(x==0)return res;
}
void Update(int x, int v){
    for (;x<=N; x+=lowbit(x))C[x]+=v;
}
void IUpdate(int s, int t, int v){
    Update(t+1, -v); Update(s, v);
}

int a[maxn], X[maxn], value[maxn];
vector<int > L[maxn], R[maxn];

int ans[maxn];
int main()
{
    int T; scanf("%d", &T);
    for(int I=1; I<=T; ++I)
    {
        int n, m; scanf("%d%d", &n, &m);
        for (int i=0; i<n; ++i)
        {
            scanf("%d", a+i);
            L[i].clear();
            R[i].clear();
        }
        for (int i=0; i<m; ++i)
        {
            int aa, b, c; scanf("%d%d%d", &aa, &b, &c);
            L[aa].pb(i);
            R[b].pb(i);
            X[i]=value[i]=c;
        }
        memset (C, 0, sizeof(C));
        memset (ans, 0, sizeof(ans));
        sort(X, X+n);
        int xcnt=unique(X, X+n)-X;
        ///printf("%d\n", xcnt);
        N=xcnt+1;
        
        for (int i=0; i<n; ++i)
        {
            for (int j=0; j<L[i].size(); ++j)
            {
                int id=L[i][j];
                int p=lower_bound(X, X+xcnt, value[id])-X+1;
                //printf("l==%d id==%d\n", p, id);
                ans[id]=Query(p);
            }
            int pp=lower_bound(X, X+xcnt, a[i])-X+1;
            
            IUpdate(pp, N, 1);
            for (int j=0; j<R[i].size(); ++j)
            {
                int id=R[i][j];
                int p=lower_bound(X, X+xcnt,value[id])-X+1;
                //..printf("p==%d  id=%d   r==%d, l==%d\n", p, id, Query(p), ans[id]);
                ans[id]=Query(p)-ans[id];
            }
        }
        ///printf("n===%d\n", n);
        printf("Case %d:\n", I);
        for (int i=0; i<m; ++i)
            printf("%d\n", ans[i]);
    }
    return 0;
}



1010  hdu 4419 Colourful Rectangle (堆式线段树)

比赛的时候很轻易想到了用3棵树维护染色区间, 7个树维护每种颜色的覆盖长度, 每次更新时根据当前点的被覆盖状态去更新长度, 但很快发现问了, 矩形面积并的算法cover的增减区间是对称的所以不需要下传标记, 但是后来发现不仅是不需要,而且是不能下传标记, 如果标记信息下传,比如+1的信息下传后, 下次来的对称区间的-1信息不会及时传到原来的区间被下传的区间,就会发生更新颜色的区间的判断错误, 但是如果不下传又没法处理交叉染色的区间的实际颜色(会被在线段树上方的颜色覆盖),这就悲剧de卡了3小时(其实还是源于对扫描线的不正确理解)。

赛后想了2天还是没解决标记下传的问题。

后来还是看了别人的代码, 和我一个思路的建树的人, 他们也没有标记下传而是对区间更新时直接做了特殊的转移, 对于当前点的状态state,如果是0, 则将当前点的状态更新为2个儿子状态的和, 如果不是, 则找出儿子区间里各颜色i(1<=i<=7)的长度, 将当前区间更新为2类, 一类是state|i的长度变成r[i]+l[i], 一类是对state的长度在总长度里减去r[i]+l[i], r[i],l[i]为儿子区间的i颜色实际覆盖长度。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn=10000+123;//n=10000
struct Segm{
    int ymin, ymax;//线段覆盖的区间,当然是离散后的坐标区间
    /// 这里保存的是每个值i表示的是i-1到i代表的点之间的距离,因此ymin要+1;
    long long x;//纪录当前线段在x轴的位置
    int s;// 记录当前线段的进出 1  -1
    int color;
    bool operator < (const Segm & a) const
    {
        return x<a.x;
    }
}seg[maxn];
long long T[maxn<<2][8];//实际覆盖的区域
int cover[maxn<<2][4];// 记录被覆盖的次数
long long len[maxn<<2];//离散后 线段树节点表示的实际长度
int n, M, h;

struct Map{
    long long y;//对y离散
    int ind;
    bool operator < (const Map & a) const
    {
        return y<a.y;
    }
}map[maxn];

int bit(int x)/// get highest 1 in bit-number
{
    if(x==0)return 0;
    int n=31;
    if((x>>16)==0){n-=16; x<<=16;}
    if((x>>24)==0){n-=8; x<<=8;}
    if((x>>28)==0){n-=4; x<<=4;}
    if((x>>30)==0){n-=2; x<<=2;}
    return n-(x>>31);
}

int change[400];

void init()
{
    int cnt=0;
    change['R']=0; change['G']=1; change['B']=2;
//    memset (seg, 0, sizeof(seg));
//    memset (map, 0, sizeof(map));
    for (int i=0; i<n; ++i)
    {
        char cc[5]; scanf("%s", cc);
        seg[i<<1].color=seg[i<<1|1].color=change[cc[0]];
        
        scanf("%I64d %I64d %I64d %I64d", &seg[i<<1].x, &map[i<<1].y, &seg[i<<1|1].x, &map[i<<1|1].y);
        map[i<<1].ind=i<<1; map[i<<1|1].ind=i<<1|1;
        seg[i<<1].s=1; seg[i<<1|1].s=-1;
    }
    sort(map, map+n+n);
    for (int i=0; i<n+n; ++i)
    {
        if(i && map[i].y!=map[i-1].y)
            len[cnt++]=map[i].y-map[i-1].y;//这里在区间上直接去重
        int num=map[i].ind>>1;
        if(map[i].ind&1)
            seg[num<<1].ymax=seg[num<<1|1].ymax=cnt-1;
        else
            seg[num<<1].ymin=seg[num<<1|1].ymin=cnt;
    }
    sort (seg, seg+n+n);
    h=bit(cnt);
    M=1<<h;
    ///printf("cnt==%d  M==%d\n", cnt, M);
    memset (T, 0, sizeof(T));
    memset (cover, 0, sizeof(cover));
    for (int i=M+cnt; i>=M; --i)
        len[i]=len[i-M];
    for (int i=M-1; i>0; --i)
        len[i]=len[i<<1]+len[i<<1|1];
    ///len[0]=0;
}

int getcolor(int x)
{
    int state=0;
    for (int i=0; i<3; ++i)
    {
        if(cover[x][i]>0)state|=1<<i;
    }
    return state;
}

void merge(const int &x)
{
    int state=getcolor(x);
    if(state)
    {
        for (int i=1; i<=7; ++i)T[x][i]=0;
        T[x][state]=len[x];
        for (int i=1; i<=7; ++i)if((i|state)!=state)
        {
            
            long long tmp=T[x<<1][i]+T[x<<1|1][i];
            T[x][i|state]+=tmp;
            T[x][state]-=tmp;
    //        if(state==i)T[x][i]=len[x];
    //        else T[x][i]=(x>=M? 0: T[x<<1][i]+T[x<<1|1][i]);
        }
    }
    else 
        for (int i=1; i<=7; ++i)
            T[x][i]=T[x<<1][i]+T[x<<1|1][i];
}

void Updata(const int &x, const int &cl, const int &v)///根据颜色更新。 
{
    ///printf("x=%d state==%d l==%I64d\n", x, state, len[x]);
    cover[x][cl]+=v;
    merge(x);
}

void IU(int l, int r, int v, int cl)
{///不需下传, 因为+1与-1的区间是对称的,有加必有减(不知这样理解对不)
    for (l+=M-1, r+=M+1; l^r^1; l>>=1, r>>=1, merge(l), merge(r))
    {
        if(~l&1)Updata(l^1, cl, v);
        if( r&1)Updata(r^1, cl, v);
    }
    while (l>1)
    {
        l>>=1, r>>=1;
        if(l^r)merge(r);
        merge(l);
    }
}
int id[7]={1, 2, 4, 3, 5, 6, 7};

int main ()
{
    int cas; scanf("%d", &cas);
    for (int I=1; I<=cas; ++I)
    {
        scanf("%d", &n);
        init();
        n<<=1;
        long long area[8]={0};
        long long now=seg[0].x;
        for (int i=0; i<n; ++i)
        {//更新区间插入或删除线段, 记录上次的位置
            Segm &a = seg[i];
//            printf("i = %d  color= %d\n", i, a.color);
            for (int i=1; i<=7; ++i)
            {
                area[i]+=T[1][i]*(a.x-now);
                if(T[1][i]==0 || a.x==now)continue;
//                printf("color==%d now==%I64d, x=%I64d, S=%I64d ", i, now, a.x, area[i]);
//                printf("Ti= %I64d\n", T[1][i]);
            }
            //area[]+=T[1]*(a.x-now);//第一为0
            IU(a.ymin, a.ymax, a.s, a.color);
            now=a.x;
        }
        
        for (int i=0; i<7; ++i)
        {
            printf("%I64d\n", area[id[i]]);
        }
    }
    return 0;
}
/*
3
2
R 0 0 2 2
G 1 1 3 3 
3
R 0 0 4 4
G 2 0 6 4
B 0 2 6 6
3
G 2 0 3 8
G 1 0 6 1
B 4 2 7 7

*/



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值