CF2020D

题目大意:有n个点,进行m次操作
每次操作有a,b,c三个参数,意思是将(a,a+b,a+2*b....a+c * b)这c+1个数合并
求最后有多少不同的连通块
a,c<=2e5,b<=10

看数据范围很容易想到要从b入手,即最多分成10组,然后我就不会了
正确思路是令f[x][y]表示以x为起点,第y组的最大值,即能到达的最靠后的位置
我们可以先把f数组统计下来,然后最后一起处理

每次处理将不同间距的操作分开处理,即分成10组,假设这里用i循环表示当前处理的操作全都是间距为i的。每一组处理中又可以把整个数列分成i组。我们对每一组的x分别进行枚举,并记录f[x][i]的最大值,若大于等于当前位置说明前面的点可以和当前点合并,否则不行。

代码

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10;
const int mod=1e9+7;

int bin[maxn];
int baba(int x)
{
	while(x!=bin[x]) x=bin[x]=bin[bin[x]];
	return bin[x];
}
void add(int a,int b)
{
	int x=baba(a),y=baba(b);
	if(x!=y) bin[x]=y;
}
int n,m;
int f[maxn][12];

void youlinaixu()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) bin[i]=i;
	for(int i=1;i<=n;i++) for(int j=1;j<=10;j++) f[i][j]=0;
	for(int i=1;i<=m;i++)
	{
		int a,d,k;
		cin>>a>>d>>k;
		f[a][d]=max(f[a][d],a+d*k);
	}
	int ans=0;
	for(int i=1;i<=10;i++)
	{
		for(int j=1;j<=i;j++)
		{
			int t=0;
			for(int x=j;x<=n;x+=i)
			{
				if(t>=x) add(x,x-i);
				t=max(t,f[x][i]);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(baba(i)==i) ans++;
	}
	cout<<ans<<endl;
}
signed main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	cin>>t;
	while(t--)
	{
	    youlinaixu();
    }
    return 0;
}

假如b更大的话或许可以考虑根号分治,即当间距<=sqrt(n)时采用这种方法,大于时每一组的点都很少,直接暴力
posted @ 2025-05-20 14:42  miku今天吃什么  阅读(9)  评论(0)    收藏  举报