D. R2D2 and Droid Army 线段树+二分 Codeforces Round #291 (Div. 2)

D. R2D2 and Droid Army
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

An army of n droids is lined up in one row. Each droid is described by m integers a1, a2, ..., am, where ai is the number of details of thei-th type in this droid's mechanism. R2-D2 wants to destroy the sequence of consecutive droids of maximum length. He has m weapons, the i-th weapon can affect all the droids in the army by destroying one detail of the i-th type (if the droid doesn't have details of this type, nothing happens to it).

A droid is considered to be destroyed when all of its details are destroyed. R2-D2 can make at most k shots. How many shots from the weapon of what type should R2-D2 make to destroy the sequence of consecutive droids of maximum length?

Input

The first line contains three integers n, m, k (1 ≤ n ≤ 1051 ≤ m ≤ 50 ≤ k ≤ 109) — the number of droids, the number of detail types and the number of available shots, respectively.

Next n lines follow describing the droids. Each line contains m integers a1, a2, ..., am (0 ≤ ai ≤ 108), where ai is the number of details of the i-th type for the respective robot.

Output

Print m space-separated integers, where the i-th number is the number of shots from the weapon of the i-th type that the robot should make to destroy the subsequence of consecutive droids of the maximum length.

If there are multiple optimal solutions, print any of them.

It is not necessary to make exactly k shots, the number of shots can be less.

Sample test(s)
input
5 2 4
4 0
1 2
2 1
0 2
1 3
output
2 2
input
3 2 4
1 2
1 3
2 2
output
1 3
Note

In the first test the second, third and fourth droids will be destroyed.

In the second test the first and second droids will be destroyed.


题意:n个机器人,每个机器人有m个值,你有k次射击机会,每一次射击可以使所有机器人的某一个值减少1,一个机器人的m个值全为0时视为该机器人被摧毁了,问在能破坏的最大的机器人数目下(机器人要连续),m个值分别对应的射击次数。
思路:先构造线段树方便以后查询区间最大值,二分能破坏的数目即可。二分判断时枚举起点,看在区间长度为mid的情况下能否用k次射击使这mid个机器人被破坏,能则说明mid可以进一步扩大(实际最大区间长度在mid右边,令l=mid+1)返回true,否则返回false(在左边,令r=mid-1)
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define maxn 100005
#define MAXN 2005
#define mod 1000000009
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define FRE(i,a,b)  for(i = a; i <= b; i++)
#define FRL(i,a,b)  for(i = a; i < b; i++)
#define mem(t, v)   memset ((t) , v, sizeof(t))
#define sf(n)       scanf("%d", &n)
#define sff(a,b)    scanf("%d %d", &a, &b)
#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
#define pf          printf
#define DBG         pf("Hi\n")
typedef long long ll;
using namespace std;

struct Node
{
    int l,r,maxx[6];
}node[maxn<<2];

int a[maxn][6];
int n,m,k;
int tmp[6],ans[6];

void pushup(int rt)
{
    int i;
    FRE(i,1,m)
        node[rt].maxx[i]=max( node[rt<<1].maxx[i] , node[rt<<1|1].maxx[i] );
}

void build(int rt,int l,int r)  //建线段树
{
    int i;
    node[rt].l=l;
    node[rt].r=r;
    if (l==r)
    {
        FRE(i,1,m)
            node[rt].maxx[i]=a[r][i];
        return ;
    }
    int mid=(l+r)/2;
    build(lson);
    build(rson);
    pushup(rt);
    return ;
}

void query(int rt,int l,int r)   //区间查询
{
    if (l<=node[rt].l&&node[rt].r<=r)
    {
        int i;
        FRE(i,1,m)
        tmp[i]=max(tmp[i],node[rt].maxx[i]);
        return ;
    }
    int mid=(node[rt].l+node[rt].r)>>1;
    if (mid>=r)
        query(rt<<1,l,r);
    else if (mid<l)
        query(rt<<1|1,l,r);
    else
    {
        query(lson);
        query(rson);
    }
    return ;
}

bool ok(int mid)
{
    bool flag=false;
    int i,j;
    FRE(i,1,n-mid+1) //枚举起点
    {
        int num=0;
        query(1,i,i+mid-1);
        FRE(j,1,m)
            num+=tmp[j];
//        printf("num=%d\n",num);
        if (num<=k)
        {
            flag=true;
            memcpy(ans,tmp,sizeof(tmp));
            break;
        }
        mem(tmp,0);
    }
    if (flag) return true;
    return false;
}

void solve()    //二分区间长度
{
    int l=1,r=n;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (ok(mid))
            l=mid+1;
        else
            r=mid-1;
    }
    int i;
    pf("%d",ans[1]);
    FRE(i,2,m)
        pf(" %d",ans[i]);
    pf("\n");
}

int main()
{
    int i,j;
    while (~sfff(n,m,k))
    {
        FRE(i,1,n)
        FRE(j,1,m)
        scanf("%d",&a[i][j]);
        build(1,1,n);
        solve();
    }
    return 0;
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值