IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 杭电第三场 题解 -> 正文阅读

[数据结构与算法]杭电第三场 题解

比赛传送门
作者: fn

比赛概况
本场比赛偏难。第1004题需要常数较小的解法。


签到题

1011题 Segment Tree with Pruning 线段树剪枝

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

考察内容
数据结构概念

分析
先通过判断左右长度是否相等判断线段树是不是完全二叉树。假设线段树上完全二叉树部分有 n n n 层,这部分的结点数就是 2 n ? 1 2^{n}-1 2n?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(){ // AC
	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; // 上半部分是完全二叉树,倒数第二层有n-hight_bit(n/k)*k个结点的区间会超过k 
			ans+=(n-hight_bit(n/k)*k)*2; // 每个区间超过k的结点在最下面一层长出两个叶子 
		}
		cout<<ans<<endl;
	}
	return 0;
}

基本题

1007题 Photoshop Layers Photoshop图层

题目大意
背景图层的颜色是 ( R = 0 , G = 0 , B = 0 ) (R=0,G=0,B=0) (R=0,G=0,B=0)
按顺序给出若干正常图层和渐变图层,正常图层直接覆盖之前的图层,渐变图层 ( R i , G i , B i ) (Ri,Gi,Bi) (Ri,Gi,Bi) 会把颜色 ( R p , G p , B p ) (Rp,Gp,Bp) (Rp,Gp,Bp) 变为 ( m i n ( R p + R i , 255 ) , m i n ( G p + G i , 255 ) , m i n ( B p + B i , 255 ) ) (min(Rp+Ri,255), min(Gp+Gi,255), min(Bp+Bi,255)) (min(Rp+Ri,255),min(Gp+Gi,255),min(Bp+Bi,255))

进行 q q q 次询问,每次询问 [ l i , r i ] [li,ri] [li,ri] 图层的颜色。

考察内容
前缀和

分析
预处理出 n t [ i ] nt[i] nt[i] 表示图层 i i i 左侧第一个正常图层。对于每个询问 [ l , r ] [l,r] [l,r],求出 r r r
侧第一个正常图层 f r fr fr,则中间的部分都是 渐变图层,可以用前缀和求出结
果,最后与 255 取最小值。
时间复杂度 O ( n + q ) O(n + q) O(n+q)

细节
读入字母为大写的16进制:

 scanf("%X", &a);   

完整代码

#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));
            }
        }
    }
}

进阶题

1004题 Game on Plane 飞机上的游戏

题目大意
给出 n n n 条直线,爱丽丝将在所有的n条直线中选择 k k k 条直线 l 1 , l 2 , . . . , l k l1,l2,...,lk l1,l2,...,lk ,然后鲍勃将画一条直线L,使 L L L l 1 , l 2 , . . . , l k l1,l2,...,lk l1,l2,...,lk 的交点最少。
爱丽丝的选择会让交点尽可能多。

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

考察内容
思维,复杂度优化

分析
两条直线存在公共点当且仅当它们重合或者它们斜率不同,因此 Bob 的最优策略一定是避
开斜率出现次数最多的那些直线。Alice 为了让 Bob 与尽量多的直线相交,最优策略就是最小
化斜率出现次数的最大值,所以不断从每种斜率的直线中各选一种即可。
时间复杂度 O ( n l o g n ) O(n log n) O(nlogn)

#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); 
    }
  }
}

高阶题

1005题 Kart Race 卡丁车比赛

题目大意
地图由 n n n 个不同的十字路口和 m m m 条单行道组成。其中第i个交叉口位于 ( x i , y i ) (xi,yi) (xi,yi) 。街道网络中没有循环,人们只能开到一些 x x x 坐标值严格较大的交叉口。街道只能在交叉口相交。

比赛将从第 1 个交叉点开始,在第 n n n 个交叉点结束。选手们可以自己选择路线,但他们只能沿着地图上标记的街道行驶。保证一个人可以从 1 到达任何地方,而任何地方都可以到达 n n n ,所以任何路线都是有效的。

每个路口都有一个设置横幅的位置,如果你选择在第 i i i 个路口设置横幅,比赛公司就会得到 w i wi wi 利润。你是比赛公司的一个中间人,工作是选择一些路口设置横幅,使总利润达到最大。因为没有赛车手愿意看到多于一条的横幅,所以对于从 1 到 n n n 的每一条可能的路线,最多选择一个交叉口。

如果有多个最优解,输出字典序最小的。

样例输入

2
6 6
0 1 1
2 2 1
1 0 1
1 2 1
2 0 1
3 1 1
1 4
3 5
2 6
5 6
1 3
4 2
2 1
0 0 8
1 1 9
1 2

样例输出

2
2 3
9
2

考察内容
主席树,树状数组,dp

分析
从 1 号点开始 DFS 整个图,并把出栈序列记下来,那么若 x 能到达 y,显然 x 晚于 y 出
栈。因为图是平面图,考虑最有代表性的两种遍历方式:顺时针遍历和逆时针遍历,那么可以
得到两个出栈序列。设 a i a_{i} ai? 表示顺时针遍历图时 i 点的出栈序, b i b_{i} bi? 表示逆时针遍历图时 i 点的出
栈序,那么 x 能到达 y 当且仅当 a x a_{x} ax? > a y a_{y} ay? b x b_{x} bx? > b y b_{y} by?

按照题意,选出来的点应当满足两两不可达,因此把 a i a_{i} ai? 看作横坐标, b i b_{i} bi? 看作纵坐标,那么
选了 ( a i , b i ) (a_{i}, b_{i}) (ai?,bi?) 就不能选它左下角以及右上角的所有点,因此选出来的点一定满足从左往右 a 递增
且 b 递减,问题转化为求价值和最大的下降子序列,可以用树状数组优化朴素 DP 在 O ( n l o g n ) O(n log n) O(nlogn)
的时间内得到最优解。

对于字典序最小最优解的求解,有一个方法是在 DP 值里直接用长度为 n 的 01 串来记录
当前方案里选了哪些点,转移的时候需要支持字典序大小的比较、拷贝以及把单点从 0 修改成
1。因此考虑使用可持久线段树来记录方案,那么单点修改的时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn),比较两个
方案的字典序时根据左子树是否相同来决定往左还是往右递归比较,时间复杂度也为 O ( n l o g n ) O(n log n) O(nlogn)
在这里,注意到每个点只会加入一次,因此如果两棵线段树的根节点的指针不同,那么表示的
01 串一定不同,不需要额外维护 H a s h Hash Hash 值。

时间复杂度 O ( n l o g 2 n ) O(n log^2 n) O(nlog2n)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005, M = N * 21;
int Case, n, m, i, x, y, w[N], cnt, dfn1[N], dfn2[N], q[N], fin[N]; bool vis[N];
vector<int>g[N];
struct P {
	int x, y;
	P() {}
	P(int _x, int _y) { x = _x, y = _y; }
	P operator-(const P&b)const { return P(x - b.x, y - b.y); }
}a[N], pivot;
int tot, l[M], r[M];
struct E {
	ll sum; int root;
	E() {}
	E(ll _sum, int _root) { sum = _sum, root = _root; }
}bit[N], tmp, ans;
inline ll cross(const P&a, const P&b) { return 1LL * a.x*b.y - 1LL * a.y*b.x; }
inline bool cmp(int x, int y) { return cross(a[x] - pivot, a[y] - pivot)>0; }
int ins(int x, int a, int b, int c) {
	int y = ++tot;
	if (a == b)return y;
	int mid = (a + b) >> 1;
	if (c <= mid)l[y] = ins(l[x], a, mid, c), r[y] = r[x];
	else l[y] = l[x], r[y] = ins(r[x], mid + 1, b, c);
	return y;
}
inline bool smaller(int x, int y) {
	if (x == y)return 0;
	int a = 1, b = n, mid;
	while (a<b) {
		mid = (a + b) >> 1;
		if (l[x] == l[y]) {
			a = mid + 1;
			x = r[x];
			y = r[y];
		}
		else {
			b = mid;
			x = l[x];
			y = l[y];
		}
	}
	return x>y;
}
void go(int x, int a, int b) {
	if (!x)return;
	if (a == b) {
		fin[++cnt] = a;
		return;
	}
	int mid = (a + b) >> 1;
	go(l[x], a, mid);
	go(r[x], mid + 1, b);
}
inline void up(E&a, const E&b) {
	if (a.sum>b.sum)return;
	if (a.sum<b.sum) { a = b; return; }
	if (smaller(b.root, a.root))a.root = b.root;
}
void dfs1(int x) {
	if (vis[x])return;
	vis[x] = 1;
	for (int i = 0; i<g[x].size(); i++)dfs1(g[x][i]);
	dfn1[x] = ++cnt;
}
void dfs2(int x) {
	if (vis[x])return;
	vis[x] = 1;
	for (int i = ((int)g[x].size()) - 1; i >= 0; i--)dfs2(g[x][i]);
	dfn2[x] = ++cnt;
	q[cnt] = x;
}
inline void modify(int x, const E&p) { for (; x <= n; x += x&-x)up(bit[x], p); }
inline E ask(int x) { E t(0, 0); for (; x; x -= x&-x)up(t, bit[x]); return t; }
int main() {
	scanf("%d", &Case);
	while (Case--) {
		scanf("%d%d", &n, &m);
		cnt = 0;
		for (i = 0; i <= n; i++) {
			g[i].clear();
			vis[i] = 0;
			bit[i] = E(0, 0);
		}
		for (i = 1; i <= n; i++)scanf("%d%d%d", &a[i].x, &a[i].y, &w[i]);
		while (m--)scanf("%d%d", &x, &y), g[x].push_back(y);
		for (i = 1; i <= n; i++) {
			pivot = a[i];
			sort(g[i].begin(), g[i].end(), cmp);
		}
		dfs1(1);
		for (cnt = 0, i = 1; i <= n; i++)vis[i] = 0;
		dfs2(1);
		ans = E(0, 0);
		for (i = n; i; i--) {
			x = q[i];
			tmp = ask(dfn1[x]);
			tmp.sum += w[x];
			tmp.root = ins(tmp.root, 1, n, x);
			up(ans, tmp);
			modify(dfn1[x], tmp);
		}
		printf("%lld\n", ans.sum);
		cnt = 0;
		go(ans.root, 1, n);
		for (i = 1; i <= cnt; i++)printf("%d%c", fin[i], i<cnt ? ' ' : '\n');
		for (i = 0; i <= tot; i++)l[i] = r[i] = 0;
		tot = 0;
	}
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 08:04:35  更:2021-07-28 08:06:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 12:44:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码