2021暑期杭电多校3(4,7, 11)

  1. Game on plane
    理论上是水题,但是题目读错了…
    题目大意:
    爱丽丝和鲍勃在玩游戏。在这个游戏中,二维平面上有n条直线。
    首先爱丽丝会在n条直线中选出确切的k条直线l1, l2, … , lk,鲍勃将会画一条直线L.
    Bob的惩罚被定义为在{l1、l2,…,lk}中与L至少有一个公共点的数量,注意两个重叠的线也有共同点。
    Alice想要最大化Bob的惩罚而Bob想要最小化。
    你会得到这n条线,请写一个程序来预测当k=1,2,3,…,n时,Bob的惩罚,
    如果两个玩家都会做最优的方案
    简单的描述就是:A画i 条线, B画一条线, 问B最少与多少条线有交点。
    思路:
    A想最大交点个数,那么每次都选斜率不同的。B每次找斜率出现最多的就好。所以我们把斜率全部存下来,从小到大排序,则这个斜率的次数就是他能坚持的轮数。 简单来说,第一轮每种斜率的都选一下,第二轮继续这样,然后第三轮有一个用完了,那么就选不了…。我们就记录每一轮能用多少斜率。(好像不用排序…)
    代码:
#include<iostream>
#include<map>
#include<cstring>
#include<algorithm>
using namespace std;
using ll = long long;
#define int long long
const int maxn = 1e5 + 10;
int num[maxn];
map<pair<int,int>, int> mp;
int f[maxn], a[maxn];

int gcd(int a,int b) {
	return b?gcd(b,a%b):a;
}

signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		mp.clear();
		int n;
		cin >> n;
		for(int i = 1; i <= n; i++){
			int x1, y1, x2, y2;
			cin >> x1 >> y1 >> x2 >> y2;
			int dx = x2 - x1, dy = y2 - y1;			
			if(dx == 0){
				dy = 1;
			}else if(dy == 0){
				dx = 1;
			}else{
				if(dx < 0){
					dx = -dx, dy = -dy;
				}
				int d = gcd(abs(dx), abs(dy));
				dx /= d, dy /= d;
			}
			mp[{dx, dy}]++;
		}
		int j = 1;
		for(auto it = mp.begin(); it != mp.end(); it++){
			int x = it->second;
			for(int i = 1; i <= x; i++) f[i]++;
		}	
		j = 1;
		for(int i = 1; i <= n; i++){
			while(!f[j]) j++;
			f[j]--;
			cout << i - j << endl;
		}	
	}
}

7.Photoshop Layers
前缀和
题目大意:
三元组(R, G, B),是从0-255的数,现在我们给你一个操作序列,让你从(0, 0, 0)进行操作。1表示赋值,2表示相加。
思路:
就是相加,然后前缀和。只不过中间处理不太好弄,因此我们学习一下官方题解的写法,用scanf()输入,用printf()输出,然后十六进制的数用&255,>> 8 来显示。为什么这样可以呢?因为255是ff,11111111。而输入的时候就是输入的6位十六进制数,二进制中是18位。只能说很精妙。还有有些时候scanf和printf确实好用。
代码:


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

#define int long long
#define ctx cout << "xxxxxxx" << endl

const int maxn = 1e6 + 10;
int a[maxn], b[maxn], c[maxn]; // 前缀和 
int man[maxn];

int get(int *s, int l, int r){
	int pos = man[r];
	int res = 0;
	if(pos == 1 || man[l] == 1 || pos <= (r-l)){
		res = s[r];
	}else{
		res = s[r] - s[l-1];
	}
	return res >= 255 ? 255 : res;
}

signed main() {
//    freopen("C:/freopen/input.txt","r",stdin);
    int t;
    cin >> t;
    while(t--){
        memset(man, 0, sizeof(man));
        int n, q;
        scanf("%lld%lld", &n, &q);
        for(int i = 1; i <= n; i++){
            ll option, num;
            scanf("%lld%X",&option,&num);
            ll tmp1 = num & 255;
            num>>= 8;
            ll tmp2 = num & 255;
            num>>= 8;
            ll tmp3 = num;
            if(option == 1){
                a[i] = tmp1, b[i] = tmp2, c[i] = tmp3;
                man[i] = 1;
            }else{
                a[i] = a[i-1] + tmp1, b[i] = b[i-1] + tmp2, c[i] = c[i-1] + tmp3;
                man[i] = man[i-1]+1;
            }
        }
        while(q--){
            int l, r;
            scanf("%lld%lld", &l, &r);
            printf("%02X%02X%02X\n",get(c,l,r),get(b,l,r),get(a,l,r));
        }
    }
} 
  1. Segment Tree with Pruning
    题目大意:按照代码计算节点数量
Node* build(long long l, long long r) {
    Node* x = new(Node);
    if (r - l + 1 <= k) return x;
    long long mid = (l + r) / 2;
    x -> lchild = build(l, mid);
    x -> rchild = build(mid + 1, r);
    return x;
}

思路:
我们观察判断的那个地方,r-l+1。这不就是r和l之间的距离?
因此让距离小于等于k时返回。
如果l = 1,那么d = r, 我们在rchild中变一下形式,(mid+1, r) --》(1, r-mid)。
这样就好做了。
代码:

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

map<ll, ll> mp;
ll n, k;

ll build(ll x){
	if(mp[x]) return mp[x];
	if(x <= k) return 1;
	mp[x] = build((x+1)/2) + build(x - (x+1)/2) + 1;
	return mp[x];
}


int main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		cin >> n >> k;
		mp.clear();
		cout << build(n) << endl;
	}
}

注意的是+1是要加上本身的意思。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值