杭电多校第三场

Find the answer

Time Limit: 4000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1331    Accepted Submission(s): 408

Problem Description

Given a sequence of n integers called W and an integer m. For each i (1 <= i <= n), you can choose some elements Wk (1 <= k < i), and change them to zero to make ∑ij=1Wj<=m. So what's the minimum number of chosen elements to meet the requirements above?.

Input

The first line contains an integer Q --- the number of test cases. 
For each test case: 
The first line contains two integers n and m --- n represents the number of elemens in sequence W and m is as described above. 
The second line contains n integers, which means the sequence W.

1 <= Q <= 15 
1 <= n <= 2*105 
1 <= m <= 109 
For each i, 1 <= Wi <= m

Output

For each test case, you should output n integers in one line: i-th integer means the minimum number of chosen elements Wk (1 <= k < i), and change them to zero to make ∑ij=1Wj<=m.

Sample Input

2

7 15

1 2 3 4 5 6 7

5 100

80 40 40 40 60

Sample Output

0 0 0 0 0 2 3

0 1 1 2 3

 题意:输出每个i在使1~i-1之间最少元素归零(不包括i)后满足 \sum_{j=1}^{i}w[j]<=m,输出每个i的归零个数。

解法之一:贪心的想法是,对于每个i应该选1~i-1这些元素里尽可能小并且加起来小于等于M的元素,这样就保证了尽可能少的元素归零。建立线段树,每个节点维护各个前缀的区间和,以及这个区间和是有多少个元素得到。对于每个元素更新在其离散化后所得到的位置到n这段加上这个元素的值,查询的时候,查询到第一个比M-a[i]大的值的位置。

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
typedef long long LL;
const int maxn=200010;
LL sum[maxn<<2];//保存区间和
LL add1[maxn<<2];//懒惰标记
ll add[maxn<<2];
ll sum1[maxn<<2];
int to[maxn];
ll a[maxn];
struct node
{
    ll v;
    int id;
};
node w[maxn];
void pushup(int rt)
{
    sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
}
void build(int rt,int l,int r)
{
    sum[rt]=sum1[rt]=add[rt]=add1[rt]=0;
    if(l==r){
        sum[rt]=sum1[rt]=0;
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    pushup(rt);
}
void pushdown(int rt)
{
    if(add[rt]){
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum[rt<<1]+=(add[rt]);
        sum[rt<<1|1]+=(add[rt]);
        add[rt]=0;
    }
    if(add1[rt]){
        add1[rt<<1]+=add1[rt];
        add1[rt<<1|1]+=add1[rt];
        sum1[rt<<1]+=(add1[rt]);
        sum1[rt<<1|1]+=(add1[rt]);
        add1[rt]=0;
    }
}
void update(ll v,int rt,int L,int R,int l,int r)
{
    if(L>R) return ;
    if(l==r){
        sum[rt]+=v;
        sum1[rt]+=1;
        return ;
    }
    if(l>=L&&r<=R){
        sum[rt]+=v;
        add1[rt]+=1;
        add[rt]+=v;
        return ;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(mid>=L) update(v,rt<<1,L,R,l,mid);
    if(mid<R) update(v,rt<<1|1,L,R,mid+1,r);
    pushup(rt);
}
ll query(ll v,int rt,int L,int R,int l,int r)
{
    if(l==r) return sum1[rt];
    pushdown(rt);
    int mid=(l+r)>>1;
    int ans;
    if(sum[rt<<1]>v) ans=query(v,rt<<1,L,R,l,mid);
    else ans=query(v,rt<<1|1,L,R,mid+1,r);
    pushup(rt);
    return ans;

}
bool cmp(node a,node b)
{
    if(a.v==b.v) return a.id<b.id;
    else return a.v<b.v;
}
int main()
{
    int Q;
    scanf("%d",&Q);
    while(Q--){
        int n;ll m;
        scanf("%d %lld",&n,&m);
        build(1,1,n+1);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            w[i].id=i;
            w[i].v=a[i];
        }
        sort(w+1,w+n+1,cmp);
        update(1e9,1,n+1,n+1,1,n+1);
        for(int i=1;i<=n;i++){
            to[w[i].id]=i;
        }
        for(int i=1;i<=n;i++){
            int k=query(m-a[i],1,1,n+1,1,n+1);
            printf("%d ",i-k);
            update(a[i],1,to[i],n+1,1,n+1);
        }
        printf("\n");
    }
    return 0;
}

第二次再做上面这题,这次的想法也大概一样,肯定要选完前面尽量小的元素。权值线段树,每个节点维护这个权值的加和和个数,查询时就是查询前m-a[i]小的数的和。

//#include <bits/stdc++.h>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <iostream>

using namespace std;
#define ll long long
const int maxn=200010;
#define LL long long
#define inf 0x3f3f3f3f
#define mod 1000000007
//ll mod;
ll a[maxn];
ll t[maxn];
int len;
void ini_hash(int n)
{
    for(int i=1;i<=n;i++)
    {
        t[i]=a[i];
    }
    sort(t+1,t+n+1);
    len=unique(t+1,t+1+n)-t-1;
}
int getpos(ll x)
{
    return lower_bound(t+1,t+1+len,x)-t;
}
struct seg
{
    int l,r;
    ll num;
    ll sum;
}tree[maxn<<2];
void pushup(int rt)
{
    tree[rt].num=(tree[rt<<1].num+tree[rt<<1|1].num);
    tree[rt].sum=(tree[rt<<1].sum+tree[rt<<1|1].sum);
}
void build(int rt,int l,int r)
{
    tree[rt].l=l;tree[rt].r=r;
    tree[rt].num=0;tree[rt].sum=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
}
void update(int rt,int va,ll x)
{
    if(tree[rt].l==tree[rt].r)
    {
        tree[rt].num+=x;
        tree[rt].sum+=x*t[tree[rt].l];
        return;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(mid>=va) update(rt<<1,va,x);
    else update(rt<<1|1,va,x);
    pushup(rt);
}
ll query(int rt,ll kth)
{
    if(kth==0) return 0;
    if(tree[rt].l==tree[rt].r)
    {
        if(kth%t[tree[rt].l]==0||kth>=t[tree[rt].l]) return min(tree[rt].num,kth/(t[tree[rt].l]*1ll));
        return 0;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(tree[rt<<1].sum>kth) return query(rt<<1,kth);
    else return tree[rt<<1].num+query(rt<<1|1,kth-tree[rt<<1].sum);
}
int main()
{
    int Q;
    scanf("%d",&Q);
    while(Q--)
    {
        int n;ll m;
        scanf("%d %lld",&n,&m);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        ini_hash(n);
        build(1,1,len);
        for(int i=1;i<=n;i++)
        {
            if(i==1)
            {
                printf("0 ");
                update(1,getpos(a[i]),1);
            }
            else
            {
                ll ans=query(1,m-a[i]);
                printf("%lld ",1ll*i-ans-1ll);
                update(1,getpos(a[i]),1);
            }
        }
        printf("\n");
    }
    return 0;
}

K Subsequence

Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1557    Accepted Submission(s): 333

Problem Description

Master QWsin is dating with Sindar. And now they are in a restaurant, the restaurant has n dishes in order. For each dish, it has a delicious value ai. However, they can only order k times. QWsin and Sindar have a special ordering method, because they believe that by this way they can get maximum happiness value.
Specifically, for each order, they will choose a subsequence of dishes and in this subsequence, when i<j, the subsequence must satisfies ai≤aj. After a round, they will get the sum of the subsequence and they can't choose these dishes again. 
Now, master QWsin wants to know the maximum happiness value they can get but he thinks it's too easy, so he gives the problem to you. Can you answer his question?

Input

There are multiple test cases. The first line of the input contains an integer T, indicating the number of test cases. For each test case:

First line contains two positive integers n and k which are separated by spaces.

Second line contains n positive integer a1,a2,a3...an represent the delicious value of each dish.


1≤T≤5

1≤n≤2000

1≤k≤10

1≤ai≤1e5

Output

Only an integer represent the maximum happiness value they can get.

Sample Input

1

9 2

5 3 2 1 4 2 1 4 6

Sample Output

22

 题意:给出一个长度为N的序列,最多可以选K次最长上升子序列,并且元素不能重复选,问最后选择的所以上升子序列的最大值是多少。

解法之一:本人实在太菜,无法反应出原来是网络流的题(泪目)。设定一个源点S,将源点拆为s0,s1,s0向s1连一条流量为k费用为0的边,表示选择K次,对于每个元素拆为a0,a1,并且连一条流量为1,费用为-ai的边,表示这个元素只选一次并且花费-ai。再遍历所有j(j>i&&a[j]>=a[i])并从ai1向aj0连一条流量为1,费用为0 的边,所有的元素都向汇点连一条流量为1费用为0的边,跑一下费用流,结果取反即可。

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
typedef long long LL;
const int max_v=5100;
const int maxn=2100;
typedef pair<int,int> P;
struct edge{int to,cap,cost,rev;};
int V;
vector<edge> G[max_v];
int h[max_v];
int dist[max_v];
int prevv[max_v],preve[max_v];
void add_edge(int from,int to,int cap,int cost)
{
    G[from].push_back((edge){to,cap,cost,G[to].size()});
    G[to].push_back((edge){from,0,-cost,G[from].size()-1});
}
int min_cost_flow(int s,int t,int f)
{
    int res=0;
    fill(h,h+V,0);
    while(f>0){
        priority_queue <P,vector<P>,greater<P> >que;
        fill(dist,dist+V,inf);
        dist[s]=0;
        que.push(P(0,s));
        while(!que.empty()){
            P p=que.top();que.pop();
            int v=p.second;
            if(dist[v]<p.first) continue;
            for(int i=0;i<G[v].size();i++){
                edge &e=G[v][i];
                if(e.cap>0&&dist[e.to]>dist[v]+e.cost+h[v]-h[e.to]){
                    dist[e.to]=dist[v]+e.cost+h[v]-h[e.to];
                    prevv[e.to]=v;
                    preve[e.to]=i;
                    que.push(P(dist[e.to],e.to));
                }
            }
        }
        if(dist[t]==inf){
            return -1;
        }
        for(int v=1;v<V;v++) h[v]+=dist[v];
        int d=f;
        for(int v=t;v!=s;v=prevv[v]){
            d=min(d,G[prevv[v]][preve[v]].cap);
        }
        f-=d;
        res+=d*h[t];
        for(int v=t;v!=s;v=prevv[v]){
            edge &e=G[prevv[v]][preve[v]];
            e.cap-=d;
            G[v][e.rev].cap+=d;
        }
    }
    return res;
}
int a[maxn];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        int n,k;
        scanf("%d %d",&n,&k);
        for(int i=1;i<=2*n+10;i++) G[i].clear();
        V=2*n+4;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int s0=2*n+1,s1=2*n+2,t=2*n+3;
        add_edge(s0,s1,k,0);
        for(int i=1;i<=n;i++){
            add_edge(s1,i,1,0);
            add_edge(i,i+n,1,-a[i]);
            add_edge(i+n,t,1,0);
            for(int j=i+1;j<=n;j++){
                if(a[i]<=a[j]){
                    add_edge(i+n,j,1,0);
                }
            }
        }
        printf("%d\n",-min_cost_flow(s0,t,k));
    }
    return 0;
}

Distribution of books

Time Limit: 8000/8000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 991    Accepted Submission(s): 387

Problem Description

zz6d likes reading very much, so he bought a lot of books. One day, zz6d brought n books to a classroom in school. The books of zz6d is so popular that K students in the classroom want to borrow his books to read. Every book of zz6d has a number i (1<=i<=n). Every student in the classroom wants to get a continuous number books. Every book has a pleasure value, which can be 0 or even negative (causing discomfort). Now zz6d needs to distribute these books to K students. The pleasure value of each student is defined as the sum of the pleasure values of all the books he obtains.Zz6d didn't want his classmates to be too happy, so he wanted to minimize the maximum pleasure of the K classmates. zz6d can hide some last numbered books and not distribute them,which means he can just split the first x books into k parts and ignore the rest books, every part is consecutive and no two parts intersect with each other.However,every classmate must get at least one book.Now he wonders how small can the maximum pleasure of the K classmates be.

1<=T<=10 
1<=n<=2*105 
1<=k<=n 
-109<=ai<=109

Input

Input contains multiple test cases. 
The first line of the input is a single integer T which is the number of test cases. T test cases follow. 
For each test case, the first line contains two integer n,k: the number of books and the number of his classmates. The second line contains n integers a1 ,a2,…, an−1,an. (aimeans the pleasure value of book i.)∑n<=2*105.

Output

For each case, print the smallest maximum pleasure of the K classmates, and one line one case.

Sample Input

2

4 2

3 -2 4 -2

5 4

-1 -1 -1 -1 6

Sample Output

2

-1

Hint

In the first example,classmate 1 get book 1,2, classmate 2 get book 3,4.the maximum pleasure is max((3-2),(4-2))=2; In the second example,he can ignore book 5 and spilt the first 4 books into 4 parts,give them to his classmates.

题意:将n本书分成K部分,使得和最大的那部分的最大最小化。

解法之一:最大值最小化可以二分搜索答案,可以看出很容易推出dp的式子,dp[i]=max(dp[j])+1,sum[i]-x<=sum[j](假设x是最大值,dp[i]表示前i项可以分成的最大块数,sum[i]表示前缀和),朴素转移复杂度太高,可以线段树优化。将前缀和离散化到1~n中,每次二分答案都建一次线段树,找出前i项里符合条件的j。具体见代码,赛后补题时还写错了一些细节,搞得最后代码改得和标程基本一样,窝真滴菜到窒息!!!

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
typedef long long LL;
const int maxn=300020;
struct node
{
    int l,r,v;
}tree[maxn<<2];
void pushup(int rt)
{
    tree[rt].v=max(tree[rt<<1].v,tree[rt<<1|1].v);
}
void build(int rt,int l,int r)
{
    tree[rt].l=l;tree[rt].r=r;
    if(l==r){
        tree[rt].v=-1000000000;
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    pushup(rt);
    return;
}
void update(int rt,int v,int p)
{
    if(tree[rt].l==tree[rt].r){
        tree[rt].v=p;
        return;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(v<=mid){
        update(rt<<1,v,p);
    }
    else update(rt<<1|1,v,p);
    pushup(rt);
}
int query(int rt,int L,int R)
{
    if(tree[rt].l>=L&&tree[rt].r<=R){
        return tree[rt].v;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    int res=-2147483647;
    if(mid>=L) res=max(res,query(rt<<1,L,R));
    if(mid<R) res=max(res,query(rt<<1|1,L,R));
    return res;
}
int n,k;
ll sum[maxn];
ll b[maxn];
ll a[maxn];
int id[maxn];
int ma[maxn];
bool check(ll x)
{
    build(1,0,n);//建立一颗值域为0~n的权值线段树
    update(1,id[0],0);//权值为0的地方先初始化为0,因为dp[0]=0,其他地方为-1e9是因为其他地方还是不合法的值域
    for(int i=1;i<=n;i++){
        int p=lower_bound(b,b+n+1,sum[i]-x)-b;//找到第一个大于或等于sum[i]-x的sum[j]的离散化后的值
        update(1,id[i],query(1,p,n)+1);//更新到当前值域为止可以分出的最大块数,dp[i]=max(dp[j])+1

    }
    if(query(1,0,n)>=k){//可分的块数大于等于K说明答案合法

        return true;
    }
    else return false;
}
void init()
{
    memset(ma,0,sizeof ma);
    memset(sum,0,sizeof sum);
    memset(b,0,sizeof b);
    memset(id,0,sizeof id);
    memset(tree,0,sizeof tree);
    memset(a,0,sizeof a);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&k);
        init();
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];

        for(int i=0;i<=n;i++) b[i]=sum[i];

        sort(b,b+n+1);

        for(int i=0;i<=n;i++){//离散化
            int t=lower_bound(b,b+1+n,sum[i])-b;
            id[i]=t+ma[t];
            ma[t]++;
        }
        ll  l=-1e15,r=1e15;
        while(l<r)//二分答案
        {
            ll mid=(l+r)>>1;
            if(check(mid)){
                r=mid;
            }
            else l=mid+1;
        }
        printf("%lld\n",l);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值