2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解
6976 Game on Plane
题意:
有 n条直线,Alice每次选1,2,…,n条直线,Bob每次画一条直线,答案是Bob画的直线和Alice选的直线的相交数,现在Alice想要最大化答案,Bob想要最小化答案,问每次选1,2,…,n条直线的答案是多少.
思路:
首先能想到的是Alice肯定要选彼此不平行的直线,Bob肯定要选平行最多的直线画一条和它们斜率相同的直线,那么也就不难想到Alice的选法最优策略就是最小化斜率出现次数的最大值,所以不断从每种斜率的直线中各选一种,一定是在各个斜率循环跑,具体实现过程就是将各个斜率按直线数排序,从最大的开始跑即可。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>p;
p a[100010];
int i,j,k;
int f[100010];
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main(){
int t;
scanf("%d",&t);
int dx,dy;
int x1,x2,y1,y2;
while(t--){
int n;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
dx=x1-x2;dy=y1-y2;
if(dx==0) dy=1; //如果垂直于坐标轴x y=1;
else if(dy==0) dx=1; //如果垂直于坐标轴y x=1;
else{
if(dx<0) {dx*=-1;dy*=-1;} //因为要用x排序,则xy正负同时颠倒,保证了斜率不变 但又可以排序
int d=gcd(abs(dx),abs(dy));
dx/=d;dy/=d; }
a[i]=p(dx,dy); } //用pair存"斜率"(直接存坐标防止误差)
sort(a+1,a+1+n); //根据从小到大排序
for(i=1;i<=n;i++) f[i]=0; //初始化记录斜率相同数量的组数
for(i=1;i<=n;i=j)
{ //j-i描述斜率相同直线数量
for(j=i;j<=n&&a[i]==a[j];j++); //记录了当前斜率相同的有几个
for(k=1;k<=j-i;k++) f[k]++; //记录了斜率分布的个数,保存至数组中
}
for(i=j=1;i<=n;i++){
while(f[j]==0) j++; //j为最多可以平行的边数
f[j]--; //--意味该线被选取
cout<<i-j<<endl; //j来描述当前相同斜率最多的条数
}
} return 0;
}
6979 Photoshop Layers
题意:
给你n个用16进制表示的RGB三元组,q个询问,询问区间[l,r]的三元组和,如果某个三个元组的状态为1,那么直接用该三元组的值覆盖前面的值,如果是2则正常求和.
思路:
可以看出,只要所查询的区间中出现了1模式的图层,那么该区间就可以转换为这一点和右端点之间的查询,最后就可以转换成某一段由1个“1”模式和n个“2”模式相加的情况。
后面输出的时候需要判断一下选择图层的起点和与终点上一个覆盖图层的点的位置关系,如果选择的图层起始点在覆盖之前图层之前,直接输出最终的图层即可,如果在后面,那么就输出最终图层减去起点图层的前一个状态
AC代码
#include<bits/stdc++.h>
#include<cstdio>
using namespace std;
const int maxn=1e5+10;
int R[maxn],G[maxn],B[maxn];
int flag[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{ int n,q;
cin>>n>>q;
flag[0]=0;
for(int i=1;i<=n;i++)
{
int p,x;
scanf("%d%X",&p,&x); //用16进制的形式读取x
B[i]=x&255; //取后两位给B
x>>=8;
G[i]=x&255; //取中间两位给G
x>>=8;
R[i]=x; //取前两位给R
if(p==1)
{
flag[i]=i;
}
else
{
flag[i]=flag[i-1];
B[i]+=B[i-1];
G[i]+=G[i-1];
R[i]+=R[i-1];
}
}
for(int i=0;i<q;i++)
{
int l,r;
scanf("%d%d",&l,&r);
if(l<=flag[r])
printf("%02X%02X%02X\n",min(R[r],255),min(G[r],255),min(B[r],255));
else
printf("%02X%02X%02X\n",min(R[r]-R[l-1],255),min(G[r]-G[l-1],255),min(B[r]-B[l-1],255));
}
}return 0;
}
6983 Segment Tree with Pruning
题意:
对区间[1,n]建线段树,返回条件是r−l+1<=k,问建成的线段树有多少节点?
思路:
可以直接模拟建树过程,对区间长度记忆化搜索,因为区间长度相同,其子节点个数也都是相同的.
AC代码1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
ll n,k;
map<ll,ll>T;
ll build(ll n)
{
if(T.find(n)!=T.end()) return T[n];
if(n<=k) return T[n]=1;
else return T[n]=build(n/2)+build(n-n/2)+1; //建左右子树
}
int main()
{
cin>>t;
while(t--)
{
cin>>n>>k;
T.clear();
cout<<build(n)<<endl;
}return 0;
}
AC代码2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<ll,ll>mp;
ll n,k;
ll build(ll l,ll r){
if(mp.count(r-l))return mp[r-l];
if(r-l+1<=k) return 1;
ll mid=(l+r)/2,sum=1;
sum+=build(l,mid);
sum+=build(mid+1,r);
return mp[r-l]=sum;
}
int main(){
int t;
cin>>t;
while(t--){
mp.clear();
cin>>n>>k;
cout<<build(1,n)<<endl;
}return 0;
}
如果觉得写的还不错,点个赞吧^ - ^