Weak Pair HDU - 5877(树状数组+离散化)

本文介绍了一种算法,用于解决在一个带权根树中寻找满足特定条件的节点对数量问题。该算法通过离散化处理节点权重,并利用树状数组进行区间查询,实现了高效的求解。

You are given a rooted tree of N nodes, labeled from 1 to N. To the ith node a non-negative value ai is assigned.An ordered pair of nodes (u,v) is said to be weak if
(1) u is an ancestor of v (Note: In this problem a node u is not considered an ancestor of itself);
(2) au×avk

Can you find the number of weak pairs in the tree?
Input
There are multiple cases in the data set.
The first line of input contains an integer T denoting number of test cases.
For each case, the first line contains two space-separated integers, NN and kk, respectively.
The second line contains N space-separated integers, denoting a1 to aN.
Each of the subsequent lines contains two space-separated integers defining an edge connecting nodes uu and vv , where node uu is the parent of node vv.

Constrains:

1N105

0ai109

0k1018
Output
For each test case, print a single integer on a single line denoting the number of weak pairs in the tree.
Sample Input
1
2 3
1 2
1 2
Sample Output
1
犯了一个超级超级低级错误,数组开小了10倍,一直和我说超时,实在没办法就重新写了一遍才发现,wa到怀疑人生GG

见上一个同类型的题目

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<map>
#define N 100005
using namespace std;
typedef long long ll;
int n;
ll k;
ll a[N];
ll id[200005];
int cnt,num;
bool inD[N];
ll ans;
int getIndex(ll x)//手写了一个查找离散坐标的函数
{
    int l=1,r=num;
    int mid;
    while(l<=r)
    {
        mid=(r+l)>>1;
        if(id[mid]==x)
            return mid;
        if(id[mid]<x)
            l=mid+1;
        else
            r=mid-1;
    }
}
vector<int> graph[N];
int c[200005];
int lowBit(int x)
{
    return x&-x;
}
int sum(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=c[x];
        x-=lowBit(x);
    }
    return ans;
}
void change(int x,int p)
{
    while(x<=num)
    {
        c[x]+=p;
        x+=lowBit(x);
    }
}//树状数组的三个操作
void dfs(int x)
{
    int index=getIndex(a[x]);
    if(!a[x])
        ans+=sum(num);
    else
        ans+=sum(getIndex(k/a[x]));
    change(index,1);
    for(int i=0;i<graph[x].size();i++)
        dfs(graph[x][i]);
    change(index,-1);//消除多兄弟节点的影响
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%lld",&n,&k);
        cnt=0;
        ans=0;
        memset(inD,false,sizeof(inD));
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
            graph[i].clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%d",a+i);
            id[++cnt]=a[i];
            if(a[i])
                id[++cnt]=k/a[i];//离散处理 
        }
        sort(id+1,id+cnt+1);
        num=1;
        for(int i=2;i<=cnt;i++)//去重
        {
            if(id[num]!=id[i])
                id[++num]=id[i];
        }
        int x,y;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            inD[y]=true;
            graph[x].push_back(y);
        }
        for(int i=1;i<=n;i++)
        {
            if(!inD[i])//找到root节点
            {
                dfs(i);
                break;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
这是之前ai给出的修改方案: 当前的设备列表是“单列垂直滚动”,每个 `section` 的 header(即 `OnlineDeviceCardNormalHeader`)占据一整行,纵向排列。现在你想改成: > ✅ **每行显示两个设备卡片(双列网格布局)** 这本质上是从 **线性列表 → 网格布局(Grid Layout)** 的转变。 --- ## 🎯 目标效果 ``` +------------------+ +------------------+ | Device Card 1 | | Device Card 2 | | (with channels) | | (with channels) | +------------------+ +------------------+ +------------------+ +------------------+ | Device Card 3 | | Device Card 4 | | (with channels) | | (with channels) | +------------------+ +------------------+ ↓ 垂直滚动 ``` 也就是说: - 每个设备作为一个独立 item - 不再使用 `section header` 来展示设备 - 改用 `UICollectionViewCell` 实现设备卡片 - 内部仍保留通道横向滑动视图 --- ## 🔧 实现思路总览 | 步骤 | 内容 | | ---- | ------------------------------------------------------------ | | 1️⃣ | 修改数据结构:不再以 `section` 为单位,改为 flat list of devices | | 2️⃣ | 替换 UI 架构:从 “Section Header + Items” 改为 “Cell + Embedded CollectionView” | | 3️⃣ | 使用 `UICollectionViewCompositionalLayout` 实现双列网格 | | 4️⃣ | 复用现有组件:`OnlineDeviceCardNormalHeader` 可改造成普通 Cell | | 5️⃣ | 调整 Presenter:让 `ItemPresenter` 成为设备卡片本身 | --- # ✅ 分步详解 --- ## 第一步:重构数据源结构(去 Section 化) ### ❌ 当前模式(基于 Section) ```swift [SectionData<Header=LTopLevelDevice, Item=TPSSChannelInfo>] ``` → 每个 section 表示一个设备,header 是设备信息,items 是其通道 ### ✅ 新模式(扁平化) ```swift [DeviceItem] // [LTopLevelDevice] ``` → 每个 cell 显示一个设备及其所有通道预览 你需要在 `GeneralDeviceListViewController` 中提供一个新的数据源数组: ```swift var deviceList: [LTopLevelDevice] { return delegate?.allDevices() ?? [] } ``` 然后把这个列表交给新的 `UICollectionViewDataSource`。 --- ## 第二步:创建新 Cell —— 把 `OnlineDeviceCardNormalHeader` 改造成普通 Cell 你现在使用的 `OnlineDeviceCardNormalHeader` 是继承自 `UICollectionReusableView`,用于作为 header。 但我们要把它变成普通的 `UICollectionViewCell`。 ### ✅ 方案一:直接修改父类(推荐) 将 `OnlineDeviceCardNormalHeader` 改为继承自 `UICollectionViewCell`: ```swift class OnlineDeviceCardNormalHeader: UICollectionViewCell { @IBOutlet weak var deviceNameLabel: UILabel! @IBOutlet weak var coverImageView: UIImageView! @IBOutlet weak var channelListVerticalCollectionView: UICollectionView! private var channels: [TPSSChannelInfo] = [] override func awakeFromNib() { super.awakeFromNib() setupCollectionView() } private func setupCollectionView() { channelListVerticalCollectionView.delegate = self channelListVerticalCollectionView.dataSource = self channelListVerticalCollectionView.register( OnlineChannelCardCell.self, forCellWithReuseIdentifier: "OnlineChannelCardCell" ) } func apply(_ device: LTopLevelDevice, at indexPath: IndexPath) { deviceNameLabel.text = device.deviceName coverImageView.image = device.coverImage ?? UIImage(named: "default_cover") channels = device.channels ?? [] // 假设 model 提供了 channels channelListVerticalCollectionView.reloadData() } } ``` ✅ 这样它就可以被当作普通 cell 使用了! --- ## 第三步:注册并使用新 Cell 在 `viewDidLoad()` 中注册这个 cell: ```swift collectionView.register( OnlineDeviceCardNormalHeader.self, forCellWithReuseIdentifier: "OnlineDeviceCardNormalHeader" ) ``` --- ## 第四步:使用 `UICollectionViewCompositionalLayout` 实现双列网格 这是最核心的部分。 ### ✅ 创建两列等宽布局 ```swift func createTwoColumnLayout() -> UICollectionViewLayout { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(150) // 高度自适应 ) let item = NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(0.49), // 接近一半宽度,留点间距 heightDimension: .estimated(300) // 根据内容自动撑高 ) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) // 设置组间间距 group.interItemSpacing = .fixed(8) let section = NSCollectionLayoutSection(group: group) section.interGroupSpacing = 8 section.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8) section.orthogonalScrollingBehavior = .none // 禁止横向滚动 return UICollectionViewCompositionalLayout(section: section) } ``` ### 应用于 collectionView ```swift override func viewDidLoad() { super.viewDidLoad() collectionView.collectionViewLayout = createTwoColumnLayout() // 数据源切换为新的 flat list 模式 collectionView.dataSource = self // 或者新的 presenter } ``` --- ## 第五步:实现新的数据源方法(cellForItemAt) 你可以选择两种方式: ### ✅ 方式一:让 `GeneralDeviceListViewController` 自己做 DataSource ```swift extension GeneralDeviceListViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return deviceList.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell( withReuseIdentifier: "OnlineDeviceCardNormalHeader", for: indexPath ) as! OnlineDeviceCardNormalHeader let device = deviceList[indexPath.row] cell.apply(device, at: indexPath) return cell } } ``` ### ✅ 方式二:写一个新的 `GridModePresenter` 如果你希望保持架构一致,可以新建: ```swift private let gridModeDS = GridListViewPresenter< GeneralDeviceListViewController, OnlineDeviceCardNormalHeader >() ``` 其中 `GridListViewPresenter` 类似于原来的 `GeneralListViewPresenter`,但只处理 flat 列表。 --- ## 第六步:调整样式和交互 ### ✅ 添加点击事件(选中设备) 在 `OnlineDeviceCardNormalHeader` 中添加 tap gesture: ```swift override func awakeFromNib() { super.awakeFromNib() let tap = UITapGestureRecognizer(target: self, action: #selector(didTap)) self.addGestureRecognizer(tap) } @objc private func didTap() { delegate?.didSelectDevice(at: indexPath) // 可定义协议传递事件 } ``` ### ✅ 控制内层 CollectionView 高度(可选) 由于 `channelListVerticalCollectionView` 是嵌套的,建议设置固定高度或最大高度,避免影响外层 layout estimate。 ```swift // 在 storyboard 中给 channelListVerticalCollectionView 设定高度约束 // 或代码中添加: NSLayoutConstraint.activate([ channelListVerticalCollectionView.heightAnchor.constraint(equalToConstant: 120) ]) ``` --- ## ⚠️ 注意事项与避坑指南 | 问题 | 解决方案 | | ------------------------------------------- | ------------------------------------------------------------ | | ❌ `UICollectionReusableView` 不能当 Cell 用 | 改成继承 `UICollectionViewCell` | | ❌ 布局高度计算不准导致闪烁 | 给内层 collection view 设定固定高度 | | ❌ 卡片宽度不均 | 使用 `.fractionalWidth(0.49)` + `interGroupSpacing` 对齐 | | ❌ 滚动卡顿 | 启用 prefetching:<br>`collectionView.isPrefetchingEnabled = true` | | ❌ 图片异步加载延迟 | 使用 Kingfisher / SDWebImage 异步加载封面图 | --- ## ✅ 最终成果 你现在实现了: - ✅ 每行两个设备卡片 - ✅ 每个卡片包含设备基本信息 + 通道预览横滑列表 - ✅ 支持点击、复用、自动高度 - ✅ 架构清晰,易于扩展为三列或瀑布流 --- ## 💡 进阶建议 1. **支持动态列数**:根据屏幕尺寸自动切换单列/双列 2. **加入动画过渡**:切换布局时淡入淡出 3. **添加占位图**:无数据时显示 loading 或空状态 4. **性能优化**:对离屏 cell 中的 channel collectionView 暂停刷新 现在请根据这个意见,我现在要做ppt 需求分析-设备列表多列布局-解决方案,请帮我列出方案对比,原方案+具体实现方式以及现方案+具体实现方式。
最新发布
10-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值