您的位置:首页 > 理论基础 > 计算机网络

【Author : DS & MZ】 2012 ACM-ICPC 杭州网络预赛

2012-09-26 23:13 169 查看
老婆提醒的是- -该发解题报告了- -虽然是个弱菜。。。

A 计算几何

因为角度只有90 和 270 , 于是只用考虑 n = 4 6 8 的情况即可,传说特别恶心- -。。

B 网络流

传说是有上下界的费用流???图论不会

C 概率 + dp

当时比赛的时候没有写这个题目真心SB啊。。。

对于一个人在某个点的概率,那么就相当于在某个点有小数个人就可以了。

预处理:

1、离线点的位置,记录每个点上有多少个人。 我用了set + map

2、 gopre[j][i] , gonex[j][i] 数组代表 如果 第i个人是救助站的话, 之前 / 之后 到第j个人,一共需要多少花费 。n ^ 2

3、pre[i][j] 代表从第 i 个人到第 j 个人,要是只有一个救助站的话,需要多少路程期望。本来应该二分+验证的,不过有可能TLE。考虑到加了一个点的话肯定救助韩的位置要右移,所以直接一个指针跟着第二位运作就可以了。复杂度 n^2

dp:

dp[i][j] 代表前 i 个人要用 j 个救助站最小的费用期望。很明显 dp[i][j] = min{ dp[k][j - 1] + pre[k + 1][i]} n^2*m

const double eps = 1e-9;
int dcmp(double d){
    if (fabs(d) < eps) return 0;
    return d < 0 ? -1 : 1;
}

int n , m;
map<int , double> Meng;
set<int> zhu;
set<int> :: iterator iter;
const int N = 1001;
int p
;
double q
;
double dp
[51] , sumpre
 , gopre

 , gonex

 , pre

 ;
double check(int l , int r, int p){
    return gopre[l][p] + gonex[r][p];
}
void solve(){
    Meng.clear();zhu.clear();
    while (n--){
        int k , x; double y;
        scanf("%d" , &k);
        while (k--){
            scanf("%d%lf",&x,&y);
            Meng[x] += y;
            zhu.insert(x);
        }
    }
    n = 0;
    memset(pre , 0 , sizeof(pre));
    for (iter = zhu.begin() ; iter != zhu.end() ; iter ++){
        if (n) sumpre
 = sumpre[n - 1];
        p[ n ] = (*iter);
        q[ n ] = Meng[p[n ++]];
        sumpre[n - 1] += q[n - 1];
    }

    memset(gopre , 0 , sizeof(gopre));
    memset(gonex , 0 , sizeof(gonex));

    for (int i = 0 ; i < n ; ++ i){
        for (int j = i ; j >= 0 ; --j){
            if (j < i) gopre[j][i] = gopre[j + 1][i] + q[j] * (p[i] - p[j]);
        }
        for (int j = i ; j < n ; ++j){
            if (j > i) gonex[j][i] = gonex[j - 1][i] + q[j] * (p[j] - p[i]);
        }
    }

    int position = 0 ;

    for (int i = 0 ; i < n ; ++i){
        position = i;
        for (int j = i ; j < n ; ++j){
            while (position + 1 <= j){
                double resl = check(i , j , position);
                double resr = check(i , j , position + 1);
                if (dcmp(resl - resr) >=0 ) position ++;
                else break;
            }
            pre[i][j] = check(i , j , position);
        }
    }

    for (int i = 0 ; i < n ; ++i)
        for (int j = 0 ; j <= m ; ++j)
            dp[i][j] = OO;

    for (int i = 0 ; i < n ; ++i)
        dp[i][1] = pre[0][i];

    for (int i = 1 ; i < n ; ++i){
        for (int j = 2 ; j <=m && j <= i + 1; ++j){
            for (int k = j - 2 ; k < i ; ++k){
                dp[i][j] = min(dp[i][j] , dp[k][j - 1] + pre[k + 1][i]);
            }
        }
    }
    printf("%.2f\n",dp[n - 1][m]);
}
int main(){
    while (cin >> n >> m , n || m) solve();
}


E 憾失First Blood的大水题!!!!

找十字。。。尼玛很水的模拟题啊,我把一个加号写成了减号,于是我的FB啊!!!!

const int N = 100;
char str

;
int n;
bool inmap(int x, int y){
    return 0 <= x && x < n && 0 <= y && y < n;
}
const int dir[][2] = {{1 , 0} , {0 , 1} , {0 , -1} , {-1 , 0} };
bool check(int x, int y){
    int j = 0 ;
    for (;; j++){
        bool flag = false;
        for (int d = 0 ; d < 4 ; ++d){
            flag |= (inmap(x + dir[d][0] * j , y + dir[d][1] * j) && str[x + dir[d][0] * j][y + dir[d][1] * j] =='#');
        }
        if (!flag) break;
        if (flag){
            for (int d = 0 ; d < 4 ; ++d){
                flag &= (inmap(x + dir[d][0] * j , y + dir[d][1] * j) && str[x + dir[d][0] * j][y + dir[d][1] * j] =='#');
            }
        }
        if (!flag) return false;
    }
    if (j <= 1) return false;
    for (int i = 1 ; i < j ; ++i){
        int v1 = x +  i;
        int v2 = x -  i;
        if (str[v1][y-1] == '#' || str[v1][y + 1] =='#') return false;
        if (str[v2][y -1] =='#' || str[v2][y + 1] == '#') return false;
        int h1 = y +  i;
        int h2 = y - i;
        if (str[x - 1][h1] == '#' || str[x +1 ][h1] == '#') return false;
        if (str[x -1 ][h2] == '#' || str[x +1 ][h2] == '#') return false;
    }
    return true;
}
void solve(){
    for (int i = 0 ; i < n ; ++i){
        scanf("%s",str[i]);
    }
    int ans = 0;
    for (int i = 0 ; i < n ; ++i){
        for (int j = 0 ; j < n ; ++j){
            if (str[i][j] == '#') {
                if (check(i , j)) {
                    //printf("%d %d\n", i, j);
                    ++ ans;
                }
            }
        }
    }
    printf("%d\n" , ans);
}
int main(){
    while (cin >> n , n) solve();
}


F 贪心
【老婆你来搞定一下吧。。。。】

H 离线 / 划分树

离线的话,将所有的数字离线,将询问也按照c离线。每次增加一个询问的时候,把所有的 <= c 的数字都加到线段树 / 树状数组上面,然后求区间和。

划分树的话,(因为这个题目搞定了划分树)。对于某个区间,直接二分第k大 >= c 。

#define M 100011
struct Seg_Tree{
	int left,right;
	int mid() {
		return (left + right) >> 1;
	}
}tt[M*4];
int len;
int sorted[M];      //元素放入,排序
int toLeft[30][M];
int val[30][M];  //树, 读入val[0][i] , 从 1 开始

void build(int l,int r,int d,int idx) {
	tt[idx].left = l;
	tt[idx].right = r;
	if(tt[idx].left == tt[idx].right)	return ;
	int mid = tt[idx].mid();
	int lsame = mid - l + 1;//lsame表示和val_mid相等且分到左边的
	for(int i = l ; i <= r ; i ++) {
		if(val[d][i] < sorted[mid]) {
			lsame --;//先假设左边的数(mid - l + 1)个都等于val_mid,然后把实际上小于val_mid的减去
		}
	}
	int lpos = l;
	int rpos = mid+1;
	int same = 0;
	for(int i = l ; i <= r ; i ++) {
		if(i == l) {
			toLeft[d][i] = 0;//toLeft[i]表示[ tt[idx].left , i ]区域里有多少个数分到左边
		} else {
			toLeft[d][i] = toLeft[d][i-1];
		}
		if(val[d][i] < sorted[mid]) {
			toLeft[d][i] ++;
			val[d+1][lpos++] = val[d][i];
		} else if(val[d][i] > sorted[mid]) {
			val[d+1][rpos++] = val[d][i];
		} else {
			if(same < lsame) {//有lsame的数是分到左边的
				same ++;
				toLeft[d][i] ++;
				val[d+1][lpos++] = val[d][i];
			} else {
				val[d+1][rpos++] = val[d][i];
			}
		}
	}
	build(l,mid,d+1, idx << 1);
	build(mid+1,r,d+1, idx << 1 | 1);
}

int query(int l,int r,int k,int d,int idx) {
	if(l == r) {
		return val[d][l];
	}
	int s;//s表示[ l , r ]有多少个分到左边
	int ss;//ss表示 [tt[idx].left , l-1 ]有多少个分到左边
	if(l == tt[idx].left) {
		s = toLeft[d][r];
		ss = 0;
	} else {
		s = toLeft[d][r] - toLeft[d][l-1];
		ss = toLeft[d][l-1];
	}
	if(s >= k) {//有多于k个分到左边,显然去左儿子区间找第k个
		int newl = tt[idx].left + ss;
		int newr = tt[idx].left + ss + s - 1;//计算出新的映射区间
		return query(newl,newr,k,d+1, idx << 1);
	} else {
		int mid = tt[idx].mid();
		int bb = l - tt[idx].left - ss;//bb表示 [tt[idx].left , l-1 ]有多少个分到右边
		int b = r - l + 1 - s;//b表示 [l , r]有多少个分到右边
		int newl = mid + bb + 1;
		int newr = mid + bb + b;
		return query(newl,newr,k-s,d+1, idx << 1 | 1);
	}
}

int _ , __;
int n , m;
void solve(){
    scanf("%d%d" , &n, &m);
    for (int i = 1 ; i <= n ; ++i){
        scanf("%d" , &val[0][i]);   //读入树
        sorted[i] = val[0][i];      //排序
    }
    sort(sorted + 1 , sorted + n + 1);  //排序
    build(1 , n , 0 , 1);   // 建树
    printf("Case %d:\n",++__);
    while (m --){
        int l , r , h;
        scanf("%d%d%d" ,&l , &r , &h);
        l ++ ; r ++;
        int low = 1 , high = r - l + 1 , mid , ret = 0;
        do{
            mid = (low + high) >> 1;
            if (query(l , r , mid , 0 , 1) <= h){   //必须至少 k = 1
                ret = max(ret , mid);
                low = mid + 1;
            }
            else high = mid - 1;
        }while (low <= high);
        printf("%d\n", ret);
    }
}
int main(){
    cin >> _; __ = 0;
    while (_ -- ) solve();
}
/*
int main() {
	int T;
	scanf("%d",&T);
	while(T --) {
		int n , m;
		scanf("%d%d",&n,&m);
		FOR(i,1,n+1) {
			scanf("%d",&val[0][i]);
			sorted[i] = val[0][i];
		}
		sort(sorted + 1 , sorted + n + 1);
		build(1,n,0,1);
		while(m --) {
			int l,r,k;
			scanf("%d%d%d",&l,&r,&k);
			printf("%d\n",query(l,r,k,0,1));
		}
	}
	return 0;
}*/


J 容斥原理 + 矩阵面积并

一开始还想写个特别NB的线段树。。。真心SB。。。

将 {R} {B} {G} {RG} {RB} {GB} {RGB} 颜色的矩形分别作面积并。然后就是很神奇的演算!!!具体在代码里面有写。

double过不了啊!!不能偷懒不改模板- -

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <iostream>
#include <math.h>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define LL __int64
const int maxn = 22222;
int cnt[maxn << 2];
LL sum[maxn << 2];
LL X[maxn];
struct Seg {
	LL h , l , r;
	int s;
	Seg(){}
	Seg(LL a,LL b,LL c,LL d) : l(a) , r(b) , h(c) , s(d) {}
	bool operator < (const Seg &cmp) const {
		return h < cmp.h;
	}
}ss[maxn];
void PushUp(int rt,int l,int r) {
	if (cnt[rt]) sum[rt] = X[r+1] - X[l];
	else if (l == r) sum[rt] = 0;
	else sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void update(int L,int R,LL c,int l,int r,int rt) {
	if (L <= l && r <= R) {
		cnt[rt] += c;
		PushUp(rt , l , r);
		return ;
	}
	int m = (l + r) >> 1;
	if (L <= m) update(L , R , c , lson);
	if (m < R) update(L , R , c , rson);
	PushUp(rt , l , r);
}
int Bin(LL key,int n,LL X[]) {
	int l = 0 , r = n - 1;
	while (l <= r) {
		int m = (l + r) >> 1;
		if (X[m] - key == 0 ) return m;
		if (X[m] - key < 0) l = m + 1;
		else r = m - 1;
	}
	return -1;
}
int m;
LL cal(){
    sort(X , X + m);
    sort(ss , ss + m);
    int k = 1;
    for (int i = 1 ; i < m ; i ++) {
        if (X[i] != X[i-1]) X[k++] = X[i];
    }
    memset(cnt , 0 , sizeof(cnt));
    memset(sum , 0 , sizeof(sum));
    LL ret = 0;
    for (int i = 0 ; i < m - 1 ; i ++) {
        int l = Bin(ss[i].l , k , X);
        int r = Bin(ss[i].r , k , X) - 1;
        if (l <= r) update(l , r , ss[i].s , 0 , k - 1, 1);
        ret += sum[1] * (ss[i+1].h - ss[i].h);
    }
    return ret;
}
char temp[2];
const int N = 200000;
struct Meng{
    char op;
    LL a ,b ,c ,d;
    void input(){
        scanf("%s%I64d%I64d%I64d%I64d",temp ,&a,&b,&c,&d);
        op = temp[0];
    }
}p
;
int main() {
	int n , cas = 1;
	int _;
	cin >> _;
	while (_--) {
	    scanf("%d",&n);
	    LL r=0 , g=0 , b=0 , rg=0 , rb=0 , gb=0 , rgb=0;
	    for (int i = 0 ; i < n ; ++i)
            p[i].input();

        m=0;
        for (int i = 0 ; i < n ; ++i){
            if (p[i].op == 'R'){
                LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d;
                X[m] = a;
                ss[m++] = Seg(a , c , b , 1);
                X[m] = c;
                ss[m++] = Seg(a , c , d , -1);
            }
        }
        r = cal();

		m=0;
        for (int i = 0 ; i < n ; ++i){
            if (p[i].op == 'G'){
                LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d;
                X[m] = a;
                ss[m++] = Seg(a , c , b , 1);
                X[m] = c;
                ss[m++] = Seg(a , c , d , -1);
            }
        }
        g = cal();

        m=0;
        for (int i = 0 ; i < n ; ++i){
            if (p[i].op == 'B'){
                LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d;
                X[m] = a;
                ss[m++] = Seg(a , c , b , 1);
                X[m] = c;
                ss[m++] = Seg(a , c , d , -1);
            }
        }
        b = cal();

        m=0;
        for (int i = 0 ; i < n ; ++i){
            if (p[i].op == 'R' || p[i].op == 'G'){
                LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d;
                X[m] = a;
                ss[m++] = Seg(a , c , b , 1);
                X[m] = c;
                ss[m++] = Seg(a , c , d , -1);
            }
        }
        rg = cal();

        m=0;
        for (int i = 0 ; i < n ; ++i){
            if (p[i].op == 'R' || p[i].op == 'B'){
                LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d;
                X[m] = a;
                ss[m++] = Seg(a , c , b , 1);
                X[m] = c;
                ss[m++] = Seg(a , c , d , -1);
            }
        }
        rb = cal();

        m=0;
        for (int i = 0 ; i < n ; ++i){
            if (p[i].op == 'G' || p[i].op == 'B'){
                LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d;
                X[m] = a;
                ss[m++] = Seg(a , c , b , 1);
                X[m] = c;
                ss[m++] = Seg(a , c , d , -1);
            }
        }
        gb = cal();

        m=0;
        for (int i = 0 ; i < n ; ++i){
            if (1){
                LL a= p[i].a , b= p[i].b , c=p[i].c , d=p[i].d;
                X[m] = a;
                ss[m++] = Seg(a , c , b , 1);
                X[m] = c;
                ss[m++] = Seg(a , c , d , -1);
            }
        }
        rgb = cal();
        //cout << rgb << endl;
        printf("Case %d:\n", cas++);
        LL R = rgb - gb , G = rgb - rb , B = rgb - rg;
        //printf("%.0lf\n%.0lf\n%.0lf\n%.0lf\n%.0lf\n%.0lf\n%.0lf\n",r,g,b,rg,rb,gb,rgb);
        printf("%I64d\n%I64d\n%I64d\n%I64d\n%I64d\n%I64d\n%I64d\n",rgb - gb  , rgb - rb  , rgb - rg  , rgb - b - R - G  , rgb - g - R - B , rgb - r - G - B  ,
               r - (rgb - b - R - G) - (rgb - g - R - B) - R );

        //printf("\n\n");
		//printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++ , ret);
	}
	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

*/


总结:

比赛的时候还是太不沉稳了。F题以前在UESTC上面做过原题,就觉得这个题目肯定可以出。将近有两个小时完全没心思看别的!!!这是2B行为!!绝对的!!以后这种问题这个题目直接交给队友,然后怒虐别的题目。放弃一个题目不可惜,因为卡在了一个题目上面而丢掉了N个题目和各种时间才可惜- -!!还是要加强啊。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: