bread(并查集妙用/线段树)

bread
【问题描述】
今有N 个无色积木,M 次染色,每次将连续一段积木刷成某种同样的颜色。
每个积木的最终颜色为他最后一次被刷出的颜色。
如果积木从头到尾没有被粉刷过,那么他最终仍然是无色。
在第i 次染色操作中,把第(ip+q)mod N + 1 个积木和第(iq+p) mod N + 1 个积木之间的
所有积木刷成颜色i
求每个积木的最终颜色
【输入格式】
第一行四个正整数N, M, p, q.
【输出格式】
输出N 行,第i 行表示第i 块积木的最终颜色(如果最终无色则输出0)。
【样例输入】
4 3 2 4
【样例输出】
2
2
3
0
【数据规模与约定】
20% n<=1e3 m<=1e4
40% n<=1e4 m<=1e5
60% n<=5e4 m<=5e5
80% n<=3e5 m<=3e6
100% n<=1e6 m<=1e7
mp+q,mq+p 在int 范围内

题面:给一些元素,每次给一些区间染色,颜色为这次染色的序数,区间是((ip+q)%n,(0q+p)%n)),求最后染色完每个区间的颜色.

这道题大家都在喊线段树线段树,可我实在没觉得有必要第一题就打线段树
于是仔细想了一下发现和之前的有一次的第三题很像,所以就打了并查集
其实并查集只是优化了暴力
首先我们发现
(np+q)%n 和(0p+q)%n 的值是一样的,所以会形成一个换,就把m优化到了n,
然后就从后往前做起,我们每处理一个区间,给每一个点设置一个祖先值,全部赋值成最左边这个元素,这样如果下一次还扫到这个区间任意一个点,我们可以直接跳到最左边,省了不少时间。然后再并查集就可以省更多时间了。
注意一开始区间会交错

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
struct color
{
	int l,r;
} co[maxn*10];
int a[maxn],n,m,p,q,le[maxn],ans[maxn];
inline int find(int x) 
{
    if(le[x]==x) return x;
    return le[x]=find(le[x]);
}
inline void write(int x)
{
	static int stk[100],top=0;
	if (x==0) {putchar('0'); return;}
	if (x<0) {putchar('-'); x=-x;}
    while (x) {stk[++top]=x%10; x/=10;}
    while (top) putchar(stk[top--]+'0'); 
}
int main()
{
	//freopen("bread.in","r",stdin);
	//freopen("bread.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&p,&q);
	for (int i=max(m-n+1,1); i<=m; i++)
	  {
	  	co[i].l=min((i*p+q)%n+1,(i*q+p)%n+1);
		co[i].r=max((i*p+q)%n+1,(i*q+p)%n+1);
      }
    for (int i=1; i<=n; i++) le[i]=i;
    for (int i=m; i>=max(m-n+1,1); i--)
	  {
	  	int x=co[i].l,y=co[i].r;
	  	int j=y;
	  	while (j>=x)
	  	  	if (ans[j]==0) {ans[j]=i; le[j]=find(x); j--;}
	  	  	else {j=le[j]-1;}
      }
   for (int i=1; i<=n; i++) 
      write(ans[i]),puts("");
    
}

线段树

# include<iostream>
# include<cstdio>
# include<cstring> 
using namespace std;
const int MAXN=1e6+10;
int f[3*MAXN],opl,opr,opx;
int n,m,p,q,ans[MAXN];
inline void update(int x,int l,int r)
{
	if (f[x]!=0) return;
	if (opl<=l&&r<=opr) f[x]=opx; 
	if (l==r) return;
	int mid=(l+r)/2;
	if (opl<=mid) update(2*x,l,mid);
	if (opr>mid) update(2*x+1,mid+1,r);
} 
inline void get(int x,int l,int r)
{
	if (l==r) { ans[l]=f[x]; return;}
	int mid=(l+r)/2;
	get(2*x,l,mid);
	get(2*x+1,mid+1,r); 
}
void print(int x)
{
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&p,&q);
	memset(f,0,sizeof(f)); 
	for (int i=m;i>=1;i--) {
		opl=(i*p+q)%n+1;
		opr=(i*q+p)%n+1;
		if (opl>opr) swap(opl,opr);
		opx=i;
		update(1,1,n);
	}
	get(1,1,n);
	for (int i=1;i<=n;i++) print(ans[i]),putchar('\n');
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值