绳子(rope)

6 篇文章 0 订阅
3 篇文章 0 订阅

绳子(rope)

有一根绳子,从左到右分为 n 段,每一段长度相同,厚度为 1。共有 m 种颜色,第 i 段的颜色为 ai。

有两种操作:

(1)将绳子从某相邻两段的交点处折叠,将对应的段拧在一起。必须保证对应的段颜色相同,拧在一起之后的厚度为原先两段之和。例如,现在绳子的颜色为 1 2 2 1 3,厚度为 1 2 1 3 1,我们可以在第 2、3 两段的交点处折叠绳子,颜色变为 2 1 3,厚度变为 3 4 1。

(2)将某一段绳子染为任意一种颜色,代价为这一段的厚度。

  对于每种颜色,你需要求出将绳子折叠至只剩两段并且至少有一段是这种颜色的最小代价。

【输入数据】

第一行两个整数 n,m,第二行 n 个整数 a1~an。

【输出数据】

m 行每行一个整数,第 i 行表示第 i 种颜色的答案。

【样例输入 1

5 3

1 2 3 3 2

【样例输出 1

2

1

1

【样例输入 2

7 3

1 2 2 1 3 3 3

【样例输出 2

2

2

2

【样例输入 3

10 3

2 2 1 1 3 3 2 1 1 2

【样例输出 3

3

3

4

【数据范围】

Subtask 1 (8pts):n<=15,m<=10。

Subtask 2 (33pts):n<=10^5,m<=10。

Subtask 3 (12pts):n<=10^5,m<=500。

Subtask 4 (20pts):n<=10^5,m<=5000。

Subtask 5 (27pts):无特殊限制。

        对于全部数据,2<=n<=10^6,1<=m<=n,1<=ai<=m,保证 m 种颜色都至少出现一次。

 

我们可以考虑一下反着来,如果最后符合要求得是什么样的

那么每翻折一下,就会发现其实翻折方向的那个重复了两次

那么经过无数次翻折后,除了两侧的可能有例外,其他的都会是两个两个的

而且所有的这些情况最后都可以合成为只有两个,因为你可以翻折出任意形态

那么现在只需讨论几种情况,中间贪心就可以选择了

枚举每种颜色作为首选,选择贡献次数最多的另一种颜色辅助,就可以求出最小的填色数了

中间维护最大值可以O(1)做的,也可以用线段树rua

#include<bits/stdc++.h>
using namespace std;
int n,m,cnt[1000005],a[1000005],tong[1000005],maxa,other[1000005];
int another[1000005],vis[1000005],ans;
int minn(int x,int y){if(x<y) return x;return y;}
int maxx(int x,int y){if(x>y) return x;return y;}
vector <int> vec[1000005];
int read()
{
	char c;int x;
	for(c=getchar();c!='-'&&(c>'9'||c<'0');c=getchar());
	if(c=='-')
	{
		x=0;
		for(c=getchar();c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
		return -x;
	}
	else 
	{
		x=c-'0';
		for(c=getchar();c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
		return x;
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		vec[a[i]].push_back(i);
		cnt[a[i]]++;
	}
	if(m==1)
	{
		printf("0\n");
		return 0;
	}
	for(int i=1;i<=m;i++)
	tong[cnt[i]]++,maxa=maxx(maxa,cnt[i]);
	for(int i=2;i+1<=n;i+=2)
	another[i+1]=i,another[i]=i+1;
	for(int i=1;i<=n;i+=2)
	other[i+1]=i,other[i]=i+1;
	for(int i=1;i<=m;i++)
	{
		int times=0;
		for(vector <int> :: iterator it=vec[i].begin(),ed=vec[i].end();it!=ed;it++)
		{
			int y=*it;
			times++;
			if(vis[y]||!another[y]) continue;
			vis[*it]=vis[another[y]]=1;
			tong[cnt[a[y]]]--;cnt[a[y]]--;tong[cnt[a[y]]]++;
			tong[cnt[a[another[y]]]]--;cnt[a[another[y]]]--;tong[cnt[a[another[y]]]]++;
			if(!tong[maxa]) maxa--;
			if(!tong[maxa]) maxa--;
		}
		ans=n-(times+maxa);
		for(vector <int> :: iterator it=vec[i].begin(),ed=vec[i].end();it!=ed;it++)
		{
			int y=*it;
			if(!vis[y]||!another[y]) continue;
			vis[*it]=vis[another[y]]=0;
			tong[cnt[a[y]]]--;cnt[a[y]]++;tong[cnt[a[y]]]++;
			tong[cnt[a[another[y]]]]--;cnt[a[another[y]]]++;tong[cnt[a[another[y]]]]++;
			if(tong[maxa+1]) maxa++;
			if(tong[maxa+2]) maxa+=2;
		}
		times=0;
		for(vector <int> :: iterator it=vec[i].begin(),ed=vec[i].end();it!=ed;it++)
		{
			int y=*it;
			times++;
			if(vis[y]||!other[y]) continue;
			vis[*it]=vis[other[y]]=1;
			tong[cnt[a[y]]]--;cnt[a[y]]--;tong[cnt[a[y]]]++;
			tong[cnt[a[other[y]]]]--;cnt[a[other[y]]]--;tong[cnt[a[other[y]]]]++;
			if(!tong[maxa]) maxa--;
			if(!tong[maxa]) maxa--;
		}
		ans=minn(ans,n-(times+maxa));
		for(vector <int> :: iterator it=vec[i].begin(),ed=vec[i].end();it!=ed;it++)
		{
			int y=*it;
			if(!vis[y]||!other[y]) continue;
			vis[*it]=vis[other[y]]=0;
			tong[cnt[a[y]]]--;cnt[a[y]]++;tong[cnt[a[y]]]++;
			tong[cnt[a[other[y]]]]--;cnt[a[other[y]]]++;tong[cnt[a[other[y]]]]++;
			if(tong[maxa+1]) maxa++;
			if(tong[maxa+2]) maxa+=2;
		}
		printf("%d\n",ans);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值