zoukankan      html  css  js  c++  java
  • Codeforces 833B / B34D The Bakery

      OwO http://codeforces.com/contest/833/problem/B

      首先读入的时候把数据读入到2 ~ n+1的位置(因为线段树处理不到0,所以后移了一格)

      dp[i][j]代表处理到第i个位置为止,分j段能得到的最大值

      lst[i]记录第与第i个蛋糕一样的蛋糕出现的上一个位置

      则,显然dp[i][j]=min(dp[i-t][j-1]+(从i-t+1到i位置的不同种类的蛋糕个数))

      记对每一段的值有贡献的点为同一段每种类型蛋糕最左边的点(比如其中一段AAFBCCDBAB,那么对这一段有贡献的B为F后面的那个B,因为他最先出现)

      那么可以从小到大枚举k

      每一次枚举的时候根据k-1时的dp值建一个线段树,从左往右遍历蛋糕数组,扫到第i个蛋糕的时候,线段树上第t个点表示的是上一段的右端点取t时(即这一段起点取t+1)dp[i][k]的取值,那么显然这是一个区间最大值得线段树

      维护的话,扫到第i个蛋糕的时候,线段树上(lst[i],i-1)区间的值+1,因为第k-1个区间的右端点取t取(lst[i],i-1)上的点时(即第k个区间为(t+1,i),包括了第i个蛋糕,而且第i个蛋糕为该区间第一个该种蛋糕),cake[i]种类对第k个区间有贡献

      查询的话,扫到第i个蛋糕的时候,查询第k-1段右节点可以选的那一段的就可以了

      

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    const int M=5e4;
    
    int dp[M][60],nowk;
    int tree[M*3],tag[M*3];
    
    void build(int rt,int li,int ri)
    {
    	tag[rt]=0;
    	if(li==ri)
    	{
    		tree[rt]=dp[li][nowk];
    		return ;
    	}
    	int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1;
    	build(lc,li,mid);
    	build(rc,mid+1,ri);
    	tree[rt]=max(tree[lc],tree[rc]);
    }
    
    void pushdown(int rt,int li,int ri)
    {
    	if(li==ri)
    	{
    		tag[rt]=0;
    		return ;
    	}
    	int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1;
    	tree[lc]+=tag[rt];
    	tree[rc]+=tag[rt];
    	tag[lc]+=tag[rt];
    	tag[rc]+=tag[rt];
    	tag[rt]=0;
    }
    
    void update(int rt,int li,int ri,int lq,int rq,int val)	//add
    {
    	if(lq<=li && ri<=rq)
    	{
    		tag[rt]+=val;
    		tree[rt]+=val;
    		return ;
    	}
    	int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1;
    	if(tag[rt])
    		pushdown(rt,li,ri);
    	if(mid>=lq)
    		update(lc,li,mid,lq,rq,val);
    	if(mid+1<=rq)
    		update(rc,mid+1,ri,lq,rq,val);
    	tree[rt]=max(tree[lc],tree[rc]);
    }
    
    int query(int rt,int li,int ri,int lq,int rq)	//get max
    {
    	int ret=-1e9-7;
    	if(lq<=li && ri<=rq)
    		return tree[rt];
    	int mid=(li+ri)>>1,lc=(rt<<1),rc=(rt<<1)+1;
    	if(tag[rt])
    		pushdown(rt,li,ri);
    	if(mid>=lq)
    		ret=max(ret,query(lc,li,mid,lq,rq));
    	if(mid+1<=rq)
    		ret=max(ret,query(rc,mid+1,ri,lq,rq));
    	return ret;
    }
    
    int n,k;
    int cake[M];
    int lst[M],tmp[M];
    int ans;
    
    void init()
    {
    	int i,j;
    	for(i=1;i<=n+1;i++)
    		tmp[i]=1;
    	for(i=2;i<=n+1;i++)
    	{
    		lst[i]=tmp[cake[i]];
    		tmp[cake[i]]=i;
    	}
    	ans=0;
    	memset(dp,0,sizeof(dp));
    }
    
    int main()
    {
    	int i,j,tmp;
    	scanf("%d%d",&n,&k);
    	for(i=2;i<=n+1;i++)
    		scanf("%d",&cake[i]);
    	init();	
    	n++;
    	for(i=1;i<=k;i++)
    	{
    		nowk=i-1;
    		build(1,1,n);
    		for(j=i+1;j<=n;j++)
    		{
    			update(1,1,n,lst[j],j-1,1);
    			tmp=query(1,1,n,i,j-1);
    //			cout<<tmp<<endl;
    			dp[j][i]=max(dp[j][i],tmp);
    		}
    	}
    	ans=dp[n][k];
    	cout<<ans<<endl;
    	return 0;
    }
    

      

  • 相关阅读:
    骗分
    【WC2016】鏖战表达式
    emacs配置
    UOJ NOI Round #4补题
    调整法
    IOC(控制反转)与DI(依赖注入)的个人理解。
    WPF进阶技巧和实战06-控件模板
    WPF进阶技巧和实战05-样式与行为
    WPF进阶技巧和实战04-资源
    WPF进阶技巧和实战03-控件(2-特殊容器)
  • 原文地址:https://www.cnblogs.com/FxxL/p/7265897.html
Copyright © 2011-2022 走看看