980E. The Number Games(倍增,思维)

980E. The Number Games(倍增,思维)

题目链接:传送门
思路:

​ 我们转化为,从一颗树上选n-k个点,使得贡献最大,且这n-k个点两两连通。贪心的取,我们必定先取大的(因为如果可以取大的但不取必亏)。

​ 我们可以将原图变为以n为根的有根树,首先n号点必选,我们接下来探讨下面选点,我们建立倍增数组, f a [ u ] [ i ] fa[u][i] fa[u][i]代表u的第 2 i 2^i 2i个祖先的编号,我们看编号为 n − 1 n-1 n1的点。

如果编号为 n − 1 n-1 n1到编号为 n n n 的点的数量小于剩下可选的点的数量,那就不能选。

否则,我们选 n − 1 n-1 n1号点,为了连通性, n − 1 n-1 n1号点到 n n n号点之间的点都要选。

我们将选的点进行标记。

所以我们有一个算法:

先选编号为n的点,然后遍历剩下编号为的点(从大到小),假设此时编号为 i i i,如果编号为 i i i的点已经被选,则跳过,否则倍增找祖先没有被标记的最小的近的祖先,(为什么是祖先呢?,因为被标记的不可能是 i 的儿子,否则他就被选了)那么找最近被标记的点就可以用倍增。

如果之间点的数量小于可选的点的数量,就不选。否则就选,并选其之间的点。

#include<bits/stdc++.h>
#define mse(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=1e6+10;
vector<int> g[N];
int book[N];
int fa[N][20];
void bfs(int root)
{
    queue<int> qe;
    qe.push(root);
    fa[root][0]=root;
    while(!qe.empty())
    {
        int u=qe.front();qe.pop();
        for(int i=1;i<20;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
        for(int v:g[u])
        {
            if(v==fa[u][0]) continue;
            fa[v][0]=u;
            qe.push(v);
        }
    }
}
int mincost(int v)
{
    int ans=0;
    if(book[fa[v][0]]) return 1;
    int wv=v;
    for(int i=19;i>=0;--i)
    {
        if(book[fa[wv][i]]) continue;
        ans+=1<<i;
        wv=fa[wv][i];
    }
    return ans+1;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    cout.tie(0);
    int n,k;
    cin>>n>>k;
    for(int i = 1; i < n; ++i)
    {
        int u,v;
        cin>>u>>v;;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    k=n-k-1;
    bfs(n);
    book[n]=1;
    for(int i=n-1;i>=1&&k;--i)
    {
        if(book[i]) continue;
        int m=mincost(i);
        if(m<=k)
        {
            k-=m;
            int wv=i;
            while(!book[wv]){
                book[wv]=1;
                wv=fa[wv][0];
            }
        }
    }
    for(int i=1;i<=n;++i)
    {
        if(!book[i])
            cout<<i<<" ";
    }
    cout<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Sure! Here is an example implementation of the Producer class that meets your requirements: ```java import java.util.Random; public class Producer implements Runnable { private int sizeOfJobs; private int numberOfJobs; private int delayBetweenJobs; private String producerName; private PrinterManager printerManager; public Producer(int sizeOfJobs, int numberOfJobs, int delayBetweenJobs, String producerName, PrinterManager printerManager) { this.sizeOfJobs = sizeOfJobs; this.numberOfJobs = numberOfJobs; this.delayBetweenJobs = delayBetweenJobs; this.producerName = producerName; this.printerManager = printerManager; } @Override public void run() { Random rand = new Random(); for (int i = 1; i <= numberOfJobs; i++) { PrintJob job = new PrintJob(producerName + " Job #" + i, rand.nextInt(sizeOfJobs) + 1); try { printerManager.addJob(job); System.out.println(producerName + " is adding a job to the print queue."); } catch (FullQueueException e) { System.out.println(producerName + " could not add a job to the print queue: " + e.getMessage()); } try { Thread.sleep(delayBetweenJobs); } catch (InterruptedException e) { System.out.println(producerName + " was interrupted while waiting to add a job to the print queue."); } } } } ``` This class takes in four parameters in its constructor: `sizeOfJobs`, `numberOfJobs`, `delayBetweenJobs`, and `producerName`. These correspond to the instance variables you mentioned in your question. It also takes in a `PrinterManager` object, which is used to add jobs to the print queue. The `run` method generates `numberOfJobs` print jobs, with names based on the `producerName` and the job number. The number of pages in each job is randomly generated between 1 and `sizeOfJobs`. The job is added to the printer manager via the `addJob` method, which might throw a `FullQueueException`. If this occurs, the method catches the exception and prints out a message indicating that the job could not be added to the print queue. Between jobs, the producer sleeps for `delayBetweenJobs` milliseconds. I hope this helps! Let me know if you have any further questions.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值