• 【洛谷5610】[Ynoi2013] 大学(并查集)


    点此看题面

    大致题意: 给定一个序列,支持两种操作:把一个区间中(x)的倍数都除以(x);询问区间和。

    (O(sqrt n) ightarrow O(logn) ightarrow O(alpha(n)))

    一道并不难的(Ynoi),然而因为各种原因写了一个多小时。。。

    最初,看到(Ynoi),本能反应就是根号,于是就写了个愚蠢的根号做法,毫无悬念地(T)了。

    然后,想到可以使用(set),结果没想到被卡得和根号做法同分。

    最后,无可奈何去参考题解,才发现可以使用并查集,终于过了此题。(尽管预处理的复杂仍然是(O(nsqrt V))的,询问的复杂度也依旧是(O(nlogn))

    解题思路

    首先,一个暴力的思路,我们考虑对于每一个数(x),开个(set)存储所有是其倍数的元素的下标。

    每次我们只要到(x)(set)中找到在修改区间内的下标(k),枚举(a_k)原先的因数判断哪些因数不再是它的因数,并在这些因数对应的(set)中删去(k)

    考虑到一个数每被操作一次,至少会变成原先的一半,因此最多被操作(log V)次,但每一次操作还要枚举因数,复杂度难以接受。

    然后我们发现,其实每次修改没必要立刻把(k)从不再是(a_k)因数的元素的(set)中删去,只需在每次访问到(a_k)时判断其是否为当前操作数(x)的倍数即可,若不是则将其从(set)中删去。

    虽然复杂度已经得到极大优化,但仍旧难以接受。考虑到这里的(set)只有删除,而没有加入操作,完全可以用(vector+)并查集替代。

    具体地,对于每一个(vector)(原(set))开一个并查集,每当删去一个元素,就在并查集上将它和后一个元素合并,具体实现详见代码。

    最后讲讲区间求和操作,由于我们把区间修改转化成了单点修改,直接开树状数组维护即可。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define V 500000
    #define pb push_back
    using namespace std;
    int n,a[N+5],C[V+5];vector<int> P[V+5],f[V+5];
    struct TreeArray//树状数组
    {
    	long long a[N+5];I void U(RI x,CI v) {W(x<=n) a[x]+=v,x+=x&-x;}
    	I long long Q(RI x,long long t=0) {W(x) t+=a[x],x-=x&-x;return t;}
    }A;
    I int fa(CI v,CI x) {return ~f[v][x]?f[v][x]=fa(v,f[v][x]):x;}//并查集
    int main()
    {
    	#define PB(x,i) (P[x].pb(i),f[x].pb(-1))//往vector中加入元素,同时扩展并查集大小
    	RI Qt,i,j;for(scanf("%d%d",&n,&Qt),i=1;i<=n;++i)//读入
    	{
    		scanf("%d",a+i),A.U(i,a[i]),PB(a[i],i);
    		for(j=2;j*j<=a[i];++j) !(a[i]%j)&&(PB(j,i),j^(a[i]/j)&&(PB(a[i]/j,i),0));//枚举因数
    	}
    	for(i=1;i<=V;++i) C[i]=P[i].size(),f[i].pb(-1);
    	RI op,sz,k;long long l,r,x,t=0;W(Qt--)
    	{
    		scanf("%d%lld%lld",&op,&l,&r),l^=t,r^=t;//强制在线
    		if(op==2) {printf("%lld
    ",t=A.Q(r)-A.Q(l-1));continue;}//询问
    		if(scanf("%lld",&x),(x^=t)==1) continue;//x=1忽略
    		#define G(x,v) (lower_bound(P[x].begin(),P[x].end(),v)-P[x].begin())//二分
    		for(i=fa(x,G(x,l));i^C[x]&&(k=P[x][i])<=r;i=fa(x,i+1))//枚举vector中在修改区间内的位置
    			a[k]%x?(f[x][i]=fa(x,i+1),0):(A.U(k,a[k]/x-a[k]),a[k]/=x);//如果不是x的倍数就删去,否则将其除以x
    	}return 0;
    }
    
  • 相关阅读:
    c++流迭代器
    SQL Server中的临时表和表变量
    基于组件的.NET软件开发
    COM组件转换成.NET组件
    将.net组件注册为com组件
    命令提示符窗口中的快捷键
    Log4net: use Sql Server to log your application events
    Retrieving the COM class factory for component with CLSID {0002450000000000C000000000000046} failed due to the following error: 80070005.
    Office Primary Interop Assemblies
    Adding custom code to Local Reports in Visual Studio.NET 2005 (Problems & Solutions)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5610.html
Copyright © 2020-2023  润新知