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天还是没解决标记下传的问题。
#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 */