- 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));
}
}
}
- 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是要加上本身的意思。