12.9補題

rainbowsea_1發表於2020-12-15

2019 ICPC Asia Taipei-Hsinchu Regional

比賽小tip(要改進!

1.J題一定要看清資料範圍,不然就很容易想岔了

2.如果兩個人都統一了思路,一個人拿不定(沒有別的題目可以出了),可以看著一個人打程式碼,防止思路到手上,就飛了!!

3.(個人)要注意板子代表什麼,比如我的旋轉卡殼板子,以及運用板子有發生什麼比如nS發小返回0,用一次,改變引數, 我的旋轉卡殼中間n有加加操作,n的含義在本函式後面改變了。//叉積算的值就是三角形面積,(我是笨蛋

碎碎念)理解板子這麼來的,我用的就是叉積來求最遠點對的鴨,那就別直接使用的了鴨,而求最大面積用的就是叉積。

J題

題意

T組資料

給出n,m,m行,每行有n個由0或者1組成的元素

問最少選幾組,可以使得每個位置至少覆蓋了一次

0 < n <= 500,0 < m <= 15

題解

暴力做,開(1<<m)大小的陣列,這個數用二進位制表示,如果二進位制上為1,則選了該陣列。

可以用bitset做,快!!!比int快32倍左右!

#include <bits/stdc++.h>
using namespace std;
inline int count(int tmp) {
    int cnt = 0;
    while(tmp) {
        if(tmp & 1) cnt++;
        tmp >>= 1;
    }
    return cnt;
}

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        bitset<510> a[20];
        for(int i = 0; i < m; i++) {
            cin >> a[i];
        }
        int ans = n + 1;
        for(int i = 0; i < (1 << m); i++) {
            int tmp = i;
            int tmp1 = i;
            bitset<510> b(0);
            int pos = 0;

            while(tmp) {
                if(tmp & 1) {
                    b = b | a[pos];
                }
                tmp >>= 1;
                pos++;
            }
            if(b.count() == n) {
                ans = min(ans, count(tmp1));
            }
        }
        if(ans != n + 1) printf("%d\n", ans);
        else printf("-1\n");
    }
}

L題

題意

問最大四邊形的面積是多少,n < 4096

題解

建凸包,找兩個點i,j(i != j)p1為i到j的三角形面積最大點,p2為j到i的三角形面積最大點。

You may not output numbers with scientific notaions. I.e., outputting 3E8 for 300000000 is not allowed.

//要用long long,不能用double

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iomanip>
using namespace std;
typedef long long ll;
const int MAX = 5e3 + 5;

struct Point{
    ll  x, y;
    bool operator < (const Point& b) const {
		return x < b.x || (x == b.x && y < b.y);
	}
};

Point p[MAX];

ll inline cross(Point a, Point b, Point c ,Point d) {
    return (a.x - b.x) * (c.y - d.y) - (c.x - d.x)* (a.y - b.y);
}

ll inline lenn(Point a, Point b) {
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
Point ch[MAX];

int inline andrew(Point p[], Point ch[],int n) {//安德魯演算法求凸包,返回頂點數
    sort(p, p + n);
    int top = 0;
    for (int i = 0; i < n; i++) {
        while(top > 1 && cross(ch[top], ch[top - 1], p[i], ch[top]) <= 0) --top;
        ch[++top] = p[i];
    }
    int tmp = top;
    for(int i = n - 2; i >= 0; --i) {
        while((top > tmp) && cross(ch[top], ch[top - 1], p[i], ch[top]) <= 0) --top;
        ch[++top] = p[i];
    }
    if(n > 1) top--;
    return top;
}

int main() {
    ios::sync_with_stdio(0);
    int T;
    cin >>T;
    while(T--) {
    	int n;
    	cin >> n;
        for (int i = 0; i < n; i++)  cin >> p[i].x >> p[i].y;
        int cnt = andrew(p, ch, n);
        ch[0] = ch[cnt];

		if(cnt <= 4)  {
			ll ans = 0;
			for(int i = 0; i < n; i++) {
				for(int j = 0; j < n; j++) {
					for(int k = 0; k < n; k++) {
						for(int q = 0; q < n; q++) {
							if(i == j || i == k || i == q || j == k || j == q || k == q) continue;
							ll x = cross(p[i], p[j], p[k], p[q]);
							ans = max(ans, abs(x));
						}
					}
				}
			}
			if(ans & 1){
                cout << ans / 2 << ".5" << endl;
            }
            else cout << ans / 2 << endl;
		}
		else {
            ll ans = 0;
            for(int i = 0; i < cnt; i++) {
            	int p1 = (i + 1) % cnt, p2 = (i + 1) % cnt;
                //p1,p2放在外面,否則j變化,p2要每次從頭開始遍歷
                for(int j = (i + 2) % cnt; j != (i - 1 + cnt) % cnt; j = (j + 1) % cnt) {
                    if(i == j) continue;
                    
                    // i -- p1   j -- p2
                    while(cross(ch[i], ch[p1], ch[i], ch[j]) < cross(ch[i], ch[(p1 + 1) % cnt], ch[i], ch[j])) {
                    	if((p1 + 1) % cnt == j) break;
                    	p1 = (p1 + 1) % cnt;	
					}
                    while(cross(ch[i], ch[j], ch[i], ch[p2]) < cross(ch[i], ch[j], ch[i], ch[(p2 + 1) % cnt])) {
                    	if((p2 + 1) % cnt == i) break;
						p2 = (p2 + 1) % cnt;
					}
                        
                    ll x = cross(ch[i], ch[p1], ch[i], ch[j]) + cross(ch[i], ch[j], ch[i], ch[p2]);
                    ans = max(ans, x);
                }
            }
			if(ans & 1){
                cout << ans / 2 << ".5" << endl;
            }
            else cout << ans / 2 << endl;
		}
    }
}

debug//錯誤版本

// WA掉了
//原因p1,p2可能變成同一個點,這樣就變成三角形了
//下圖
ll ans = 0;
cnt++;
for(int i = 0; i < cnt; i++) {
    for(int j = 0; j < cnt; j++) {
        if(i == j) continue;
        int p1 = i + 1, p2 = j + 1;
        // i -- p1   j -- p2
        while(cross(ch[i], ch[p1], ch[i], ch[j]) < cross(ch[i], ch[(p1 + 1) % cnt], ch[i], ch[j])) {
        	if((p1 + 1) % cnt == j) break;
        	p1 = (p1 + 1) % cnt;	
		}
        while(cross(ch[j], ch[i], ch[j], ch[p2]) < cross(ch[j], ch[i], ch[j], ch[(p2 + 1) % cnt])) {
        	if((p2 + 1) % cnt == i) break;
			p2 = (p2 + 1) % cnt;
		}
            
        ll x = cross(ch[i], ch[p1], ch[i], ch[j]) + cross(ch[i], ch[j], ch[i], ch[p2]);
        ans = max(ans, x);
    }
}

在這裡插入圖片描述

// T
//原因 j變化,p2要每次從頭開始遍歷
ll ans = 0;
for(int i = 0; i < cnt; i++) {
    for(int j = (i + 2) % cnt; j != (i - 1 + cnt) % cnt; j = (j + 1) % cnt) {
        if(i == j) continue;
        int p1 = (i + 1) % cnt, p2 = (j + 1) % cnt;
        // i -- p1   j -- p2
        while(cross(ch[i], ch[p1], ch[i], ch[j]) < cross(ch[i], ch[(p1 + 1) % cnt], ch[i], ch[j])) {
        	if((p1 + 1) % cnt == j) break;
        	p1 = (p1 + 1) % cnt;	
		}
        while(cross(ch[i], ch[j], ch[i], ch[p2]) < cross(ch[i], ch[j], ch[i], ch[(p2 + 1) % cnt])) {
        	if((p2 + 1) % cnt == i) break;
			p2 = (p2 + 1) % cnt;
		}
            
        ll x = cross(ch[i], ch[p1], ch[i], ch[j]) + cross(ch[i], ch[j], ch[i], ch[p2]);
        ans = max(ans, x);
    }
}

相關文章