HGOI 20190709

第一天(前面咕了两天),210,没发挥好,凑合。

  1. 煎饼

一共切横 \(h\) 加竖 \(v\) 刀,则一共分成 \((h+1)*(v+1)\)

显然 \(@\) 总和 \(sum\)\((h+1)*(v+1)\) 的倍数,不是就不可能达到

那么每块的大小就是 \(\dfrac{sum}{(h + 1) * (v+ 1)}\)

这样就可以可以明确每行每列的刀的位置

那么其他位置用二维前缀和check一下就行

#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second
#define enter cout << endl

const int N = 110 ;

int n, m, r, c, cntr, cntc, xgm, cnt ;
int sum[N][N], a[N][N], row[N], col[N], psb[10 * N] ;
string s ;

int get(int X1, int Y1, int X2, int Y2) {
    return sum[X2][Y2] - sum[X1 - 1][Y2] - sum[X2][Y1 - 1] + sum[X1 - 1][Y1 - 1] ;  
}

void clear() {
    cnt = 0, xgm = 0 ;
    memset(sum, 0, sizeof(sum)) ;
}

signed main() {
    int t ; scanf("%d", &t) ;
    rep(rnd, 1, t) {
        clear() ;
        scanf("%d%d%d%d", &n, &m, &r, &c) ;
        rep(i, 1, n) {
            cin >> s ; 
            rep(j, 0, m - 1) a[i][j + 1] = (s[j] == '@') ? 1 : 0 ;
        }
        printf("Case #%d: ", rnd) ;
        rep(i, 1, n) 
        rep(j, 1, m)
        if (a[i][j] == 1) xgm++ ;
        rep(i, 1, n)
        rep(j, 1, m)
        sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j] ;
        if (!xgm) {
            puts("POSSIBLE") ;
            continue ;
        }
        if (xgm % (r + 1) != 0 || xgm % (c + 1) != 0) {
            puts("IMPOSSIBLE") ;
            continue ;
        }
        int sz = xgm / ((r + 1) * (c + 1)) ;
        int rowsz = xgm / (r + 1), colsz = xgm / (c + 1) ;
    //  cout << rowsz << " " << colsz << endl ;
        cntr = cntc = 0 ;
        int cur = 0 ;
        rep(i, 1, n) if (get(row[cur] + 1, 1, i, m) == rowsz) row[++cntr] = i, cur++ ;
        if (cur != r + 1) {
            puts("IMPOSSIBLE") ;
            continue ;
        }
        cur = 0 ;
        rep(j, 1, m) if (get(1, col[cur] + 1, n, j) == colsz) col[++cntc] = j, cur++ ;
        if (cur != c + 1) {
            puts("IMPOSSIBLE") ;
            continue ;
        }
        bool flg = true ;
        rep(i, 1, cntr)
        rep(j, 1, cntc)
        if (get(row[i - 1] + 1, col[j - 1] + 1, row[i], col[j]) != sz) {
            flg = false ;
            break ;
        }
        if (flg) puts("POSSIBLE") ;
        else puts("IMPOSSIBLE") ;
    }       
    return 0 ;
}

  1. 机器人

这题很简单,裸的二分,普及组难度

二分答案,然后求出没个收银员的可手的甜甜圈数量,

看前 \(R\) 大的和是否 >= \(B\)

注意数量要与0取max

#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second
#define int long long

const int N = 100010 ;

int n, k, m, cnt ;
int valid[N] ;

struct node {
	int m, s, p ; // Max = m, tim = s * n + p
} a[N] ;

bool cmp1(node a, node b) {
	return a.s < b.s ;
}

bool cmp2(node a, node b) {
	return a.p < b.p ;
}

bool cmp3(node a, node b) {
	return a.s * min(a.m, b.m) + a.p < b.s * min(a.m, b.m) + b.p ; 
}

bool check(int tm) {
	int rst = k, cnt = 0 ;
	rep(i, 1, m) {
		if (tm <= a[i].p) continue ;
		valid[++cnt] = min(a[i].m, (tm - a[i].p) / a[i].s) ;
	}
	sort(valid + 1, valid + cnt + 1) ;
	reverse(valid + 1, valid + cnt + 1) ;
	rep(i, 1, min(n, cnt)) rst -= valid[i] ;
	return rst <= 0 ; 
}

void clear() {
	
}

signed main() {
	freopen("robot.in", "r", stdin) ;
	freopen("robot.out", "w", stdout) ; 
	int t ; scanf("%lld", &t) ;
	rep(rnd, 1, t) {
		clear() ;
		scanf("%lld%lld%lld", &n, &k, &m) ; // robot, sum, sell
		rep(i, 1, m) scanf("%lld%lld%lld", &a[i].m, &a[i].s, &a[i].p) ;
	//	sort(a + 1, a + m + 1, cmp1) ;
		int l = 0, r = LONG_LONG_MAX / 2, mid ;
	//	cout<<LONG_LONG_MAX<<endl ;
		while (l < r) {
			int mid = (l + r) >> 1ll ;
			if (check(mid)) r = mid ;
			else l = mid + 1 ;
		}
//		rep(i, max(0ll, l - 5), r + 5) 
//		if (check(i)) {
//			printf("%lld\n", i) ;
//			break ;
//		} 
		printf("Case #%d: %lld\n", rnd, l) ;
	}
	
	return 0 ;
}

  1. 面包师

这个题有点小难,考试时瞎搞骗分40

其实就是个背包,只不过稍有变化

每个面包要么不切,要么切开,

切开对答案的贡献就是 \([2*min(x,y),2*\sqrt{(x^2+y^2)}]\)

所以问题就是取一些面包,然后使他们的贡献(区间)尽可能接近p

设每一个面包的贡献是 \([low,up]\), 令 \(p-\sum_{i=1}^n 2*(x+y)=k\)

首先可以观察到必定满足 \(Σlow[i]<=p\)

会观察到 \(Σlow[i]\) 范围不叫小,大概是 \(10^4\)\(10^5\) 这么大

那么可以考虑背包

dp[i]表示 \(Σlow=i\)\(Σup\) 的最大值

然后在\(dp[0,min(Σlow,p)]\) 的范围上找一个最大的,如果大于 \(p\) 就是 \(p\)

如果没有就是它自己

输出就行了

还算基础(这你都做不对!)

#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second

const int N = 100010 ;

int n, p, tot, Low ;
int w[N], h[N], t[N] ;
double delt[N], f[N] ;

signed main() {
	int T ;
	scanf("%d", &T) ;
	rep(rnd, 1, T) {
		scanf("%d%d", &n, &p) ;
		memset(t, 0, sizeof(t)) ;
		Low = tot = 0 ;
		rep(i, 1, n) {
			scanf("%d%d", &w[i], &h[i]) ;
			if (h[i] > w[i]) swap(w[i], h[i]) ;
			tot += h[i] ; Low += h[i] + w[i] ;
			delt[i] = sqrt(1.0 * w[i] * w[i] + 1.0 * h[i] * h[i]) - h[i] ;
		}
		rep(i, 0, tot) f[i] = 0 ;
		t[0] = 1 ;
		rep(i, 1, n)
		per(j, tot, h[i])
		if (t[j - h[i]])
		f[j] = max(f[j], f[j - h[i]] + delt[i]), t[j] = 1 ;
		double ans = 0 ;
		rep(i, 0, tot) if (t[i] && Low * 2 + i * 2 <= p) ans = max(ans, min((double) p, f[i] * 2 + Low * 2 + i * 2)) ;
		printf("Case #%d: %.6lf\n", rnd, ans) ;
	} 
	return 0 ;
}

posted @ 2020-07-11 22:36  harryhqg  阅读(135)  评论(1编辑  收藏  举报