小沙的算数(前缀和+二分 or 并查集)

Posted MangataTS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小沙的算数(前缀和+二分 or 并查集)相关的知识,希望对你有一定的参考价值。

题目连接

https://ac.nowcoder.com/acm/contest/23477/F

题面

思路

二分

因为符号不会发生变化,所以我们其实只需要处理两种变化即可,乘法元素的变化,加法元素的变化,对于乘法元素我们知道只要我们更改的元素的相邻的运算符是乘法那么就是乘法元素更改,如果一个都没有那么就是加法元素,我们要做的处理就是将连续乘法的元素合并在一起,把这一段长度的乘法结果放在一起,后续更新,然后更新的时候就使用逆元更新即可,因为符号不会发生改变,如果是乘法那么直接更新和就好了

并查集

其实并查集更容易维护,并查集维护的也就是我们上面的连续乘法元素的集合,加法元素为单独的一个集合,然后还是按照上述更新方式即可

代码

二分Code

#include<bits/stdc++.h>
using namespace std;
//----------------????岿??----------------
#define ll long long
#define mod 1000000007
#define endl "\\n"
#define PII pair<int,int>

int dx[4]=0,-1,0,1,dy[4]=-1,0,1,0;

ll ksm(ll a,ll b) 
	ll ans = 1;
	for(;b;b>>=1LL) 
		if(b & 1) ans = ans * a % mod;
		a = a * a % mod;
	
	return ans;


ll lowbit(ll x)return -x & x;
ll inv(ll x) return ksm(x,mod-2);

const int N = 2e6+10;
//----------------????岿??----------------
ll n,m,q,a[N];
ll sum;
char op[N];
map<ll,ll> vis;
vector<ll> Vec;

int main()

	scanf("%lld%lld",&n,&q);
	scanf(" %s",op+1);
	op[n] = op[0] = '+';
	for(int i = 1;i <= n; ++i) 
		scanf("%lld",&a[i]);
	
	for(int i = 1;i <= n;) //获取乘法操作连续的左区间和值,并获取这个算式的开始运算的值
		if(op[i] == '*')
			ll res = a[i];
			ll l = i;
			Vec.push_back(l);
			++i;
			while(i <= n && op[i] == '*')
				res = (res * a[i]) % mod;
				++i;
			
			res = (res * a[i]) % mod;
			sum = (sum + res) % mod;
			vis[l] = res;
			i++;

		
		else
			ll res = a[i];
			i++;
			while(i <= n && op[i] == '+')
				res = (res + a[i]) % mod;
				++i;
			
			sum =(sum + res) % mod;
		
	
	Vec.push_back(0x3f3f3f3f);
	ll u,v;
	while(q--)
		scanf("%lld %lld",&u,&v);
		if(op[u-1] == '*' || op[u] == '*')
			int pos = upper_bound(Vec.begin(),Vec.end(),u)-Vec.begin()-1;
			ll l = Vec[pos];
			ll kk = vis[l] % mod;
			sum = (sum - kk + mod) % mod;
			ll p = (kk * inv(a[u])) % mod;
			p = (p * v) % mod;
			a[u] = v;
			vis[l] = p;
			sum = (sum + p) % mod;
		
		else
			sum = (sum + v - a[u] + mod) % mod;
			a[u] = v;
		
		printf("%lld\\n",sum);
	

	return 0;

/*
9 10
*+*+*+*+
1 2 3 4 5 6 7 8 9

9 10
+*+*+*+*
1 2 3 4 5 6 7 8 9

1 2
3 12
5 30
7 56

4 4
***
1 2 3 4

1 24

5 4
++++
1 2 3 4 5

1 + 2 + 3 * 4 + 5

*/

并查集Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=1e6+10,mod=1e9+7;
int n,q;
int a[N];
LL s[N];
char c[N];
LL POW(LL x,LL y)
	LL re=1;
	while(y)
		if(y&1)re=re*x%mod;x=x*x%mod;y>>=1;
	
	return re;

int num[N];
int f(int x)
	return  x==a[x]?x:a[x]=f(a[x]);

void u(int x,int y)
	x=f(x),y=f(y);
	if(x!=y)a[x]=y,s[y]=(s[y]*s[x])%mod;

int main()
	scanf("%d%d",&n,&q);
	scanf("%s",c+1);
	for(int i=1;i<=n;i++)
		a[i]=i,s[i]=1;
	
	for(int i=1;i<n;i++)
		if(c[i]=='*')u(i,i+1);
	
	LL ans=0;
	for(int i=1,x;i<=n;i++)
		scanf("%d",&x);
		num[i]=x;
		s[f(i)]=(s[f(i)]*x)%mod;
	
	for(int i=1;i<=n;i++)
		if(f(i)==i)ans+=s[i];
		ans%=mod;
	
	int x,y;
	while(q--)
		scanf("%d%d",&x,&y);
		int anc=f(x);
		ans-=s[anc];
		if(ans<0)ans+=mod;
		s[anc]=s[anc]*POW(num[x],mod-2)%mod;
		s[anc]=s[anc]*y%mod;
		ans+=s[anc];
		num[x]=y;
		ans%=mod;
		printf("%lld\\n",ans);
	
 

以上是关于小沙的算数(前缀和+二分 or 并查集)的主要内容,如果未能解决你的问题,请参考以下文章

小沙的步伐(枚举+暴力)

小沙的长路(图论+数学)

NOIP2010关押罪犯[并查集|二分答案+二分图]

1821. [JSOI2010]部落划分并查集+二分

冲击蓝桥杯-并查集,前缀和,字符串

BZOJ4025 二分图 分治 并查集 二分图 并查集按秩合并 带权并查集