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;
}

浙公网安备 33010602011771号