2021杭电暑假多校联赛第三场补题

1011Segment Tree with Pruning(线段树剪枝)

题目大意:给定一棵区间 [ 1 , n ] [1,n][1,n] 的线段树,其中最长的叶子结点区间长度不超过 k kk ,求出线段树的结点数。

 解题思路:分为前最后一层与非最后一层来进行计算,非最后一层的一定是一个完全二叉树部分,结点数为2^n-1,最后加上最后一层的节点数即为答案。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t,l1=0,l2=0;
ll n,k,sum[62],x,ans,lp;

//获取最高位
ll hight_bit(ll x){
	x= x|(x>>1);              
	x= x|(x>>2);              
	x= x|(x>>4);              
	x= x|(x>>8);              
	x= x|(x>>16);             
	x= x|(x>>32);
	x= x|(x>>64);
	return (x+1) >> 1;        
}

int main(){ 
	sum[0]=1;
	for(int i=1;i<=61;i++){
		sum[i]=sum[i-1]*2;
	}
	cin>>t;
	while(t--){
		cin>>n>>k;
		x=n;lp=1;l1=1;l2=1;
		if(k==1){
			cout<<n*2-1<<endl;
			continue;
		}
		while(x>k){
			x=(x+1)/2;
			l1++;
		}
		while(n-lp+1>k){
			lp=(lp+n)/2+1;
			l2++;
		}
		if(l1==l2){
			ans=sum[l1]-1; // 完全 
		}
		else{ // 不完全 
			ans=hight_bit(n/k)*2-1; // 上半部分是完全二叉树
			ans+=(n-hight_bit(n/k)*k)*2; // 每个区间超过k的结点在最下面一层长出两个叶子 
		}
		cout<<ans<<endl;
	}
	return 0;
}

1007Photoshop Layers

 

 题目大意:

利用(R,G,B)这样的一个组合来表示颜色背景。图层的颜色是 ( R = 0 , G = 0 , B = 0 )(R=0,G=0,B=0)(R=0,G=0,B=0) 。按顺序给出若干正常图层和渐变图层,正常图层会直接覆盖之前的图层,渐变图层(Ri,Gi,Bi) (Ri,Gi,Bi)(Ri,Gi,Bi) 会把颜色(Rp,Gp,Bp) 变成(min(Rp+Ri,255),min(Gp+Gi,255),min(Bp+Bi,255))。

进行 q次询问,每次询问 [li,ri]图层的颜色。
解题思路:用一个数组来记录正常图层,每次给出区间(l,r)时找到r左边的第一个正常图层,则中间就全部为渐变图层,利用前缀和对其进行处理,根据题意与255最小值进行输出。

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

struct node {
    int b,v[3];
} p[100005];
int t,n,m,nt[100005],pr[100005][3];
int main() {
    scanf("%d",&t);
    while(t--) {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++) nt[i]=0;
        for(int i=1;i<=n;i++) pr[i][0]=pr[i][1]=pr[i][2]=0;
        for(int i=1,v;i<=n;i++) {
            scanf("%d %X",&p[i].b,&v); // 读入16进制 
            p[i].v[0]=v>>16;
            p[i].v[1]=(v>>8)-(p[i].v[0]<<8);
            p[i].v[2]=v-(p[i].v[1]<<8)-(p[i].v[0]<<16);
            
            if(p[i].b==1) { // 正常图层 
                pr[i][0]=pr[i][1]=pr[i][2]=0;
                nt[i]=i;
            } else { // 渐变图层 
                nt[i]=nt[i-1];
                pr[i][0]=pr[i-1][0]+p[i].v[0];
                pr[i][1]=pr[i-1][1]+p[i].v[1];
                pr[i][2]=pr[i-1][2]+p[i].v[2];
            }
        }
        
        for(int i=1,l,r;i<=m;i++) {
            scanf("%d %d",&l,&r);
            if(nt[r]<l) {
                printf("%02X%02X%02X\n",
                min(pr[r][0]-pr[l-1][0],255),
                min(pr[r][1]-pr[l-1][1],255),
                min(pr[r][2]-pr[l-1][2],255)); // 输出16进制 
            } else {
                printf("%02X%02X%02X\n",
                min(p[nt[r]].v[0]+pr[r][0],255),
                min(p[nt[r]].v[1]+pr[r][1],255),
                min(p[nt[r]].v[2]+pr[r][2],255));
            }
        }
    }
}

 1004Game on Plane

输入:

2
2
1 1 2 2
0 0 2 3
3
1 1 2 2
1 1 2 2
3 2 5 4

输出:

0
1
0
0
0

题目大意:给出n条直线,Alice将选择其中的k条记为l1,l2,l3,l4......lk;Bob将选择其中的一条,使得这一条直线与Alice选择的有尽可能多的交点。

求解k=1,2,3...n时的交点数目。

解题思路:很容易可以想到Alice要使得交点数目尽可能的少,她应该尽量选择斜率不同的直线,Bob要得到尽可能少的交点,因此,他每次应该避开斜率出现最多的直线(与其平行)。

因此,对于所有的点,我们计算斜率并将其化到最简,对斜率进行排序,从最小的斜率开始,将斜率相同的直线储存到数组k【d】当中,且每次都从k【0】开始储存。记录斜率我们可以使用pair,分别记录分子与分母。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>P; // 相当于结构体 
const int N=100005;
int Case,n,i,j,k,f[N];
P a[N]; // 保存分数 
inline int abs(int x){return x>0?x:-x;}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main(){
	
  scanf("%d",&Case);
  while(Case--){
  	
    scanf("%d",&n);
    for(i=1;i<=n;i++){
      int x1,y1,x2,y2;
      scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
      int dx=x2-x1,dy=y2-y1;
      if(dx==0)dy=1; // 斜率不存在 
      else if(dy==0)dx=1; // 斜率为0 
      else{
        if(dx<0)dx=-dx,dy=-dy; // 规定分母非负 
        int d=gcd(abs(dx),abs(dy));
        dx/=d,dy/=d; // 化最简分数 
      }
      a[i]=P(dx,dy); 
    }
    sort(a+1,a+n+1); // pair可以直接sort
	 
    for(i=1;i<=n;i++)f[i]=0; // 初始化f数组 
    
    for(i=1;i<=n;i=j){ // i指向一个斜率 
      for(j=i+1;j<=n&&a[i]==a[j];j++); // j指向i后面第一个 数值与i指向的斜率不同的 斜率 
      
      for(k=1;k<=j-i;k++)f[k]++;
    }
    
    for(i=j=1;i<=n;i++){
      while(f[j]==0)j++; // 跳过取完的数组 
      f[j]--; // 从f[j]取一个,此时最多j条平行线 
      printf("%d\n",i-j); 
    }
  }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值