P5012 水の数列

题面

传送门

分析

拿到这题后,你会发现这题对于询问要求强制在线,但是询问的对象是不发生改变的,所以我们可以从这个长度为 N N N的序列着手。

通过分析输出内容可以发现,数字越大原始答案越大,所以可以将 n u m num num 数组排序后 O ( n ) O(n) O(n) 地跑出所有 x x x 对应的原始答案,也就得到了每个 x x x 对应的真实答案(真实答案不是整型,所以在比较真实答案大小的时候最好使用乘法而不是除法!!!)。

对于所有 n u m i ≤ x num_i\leq x numix,我们可以先把 [ i , i ] [i,i] [i,i] 当成单独的一个区间来统计答案信息,然后再左右判断是否可以合并,合并时对答案信息的维护是显然的。

本题给的询问限制是区间个数,所以我们自然而然想到了在每个区间个数中找到最大的真实答案对应的信息,然后就是RMQ问题了,使用st表解决。

注意平方的时候会爆int,所以得开long long,而st表开long long有可能会爆,所以可以保存所有 x x x对应的答案信息然后再跑,具体实现可看代码。

代码

#include<bits/stdc++.h>
using namespace std;
/*
	需要先把所有x的答案保存下来,然后查询的时候跑st表即可 
	t1:长度平方和 
	t2:区间数量 
*/
const int N=1e6+5;
int n,q,a,b,x,y,pos[N][23],t2,lg[N],f[N],sz[N],mx;
long long t1,ans,sum[N];
bool ins[N];
struct node{
	int x,id;
}num[N];
bool cmp(node a,node b){
	return a.x<b.x;
}
int fd(int x){
	return x==f[x]?x:f[x]=fd(f[x]);
}
bool check(int x,int y,int k){
	if(pos[x][k]==0) return 0;
	if(pos[y][k]==0) return 1;
	return 1ll*sum[pos[x][k]]*pos[y][k]>1ll*sum[pos[y][k]]*pos[x][k];
}
int main(){
//	freopen(".in","r",stdin);
//	freopen("tx.out","w",stdout);
//	freopen("tx.in","r",stdin);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%d",&num[i].x);
		num[i].id=i;
		mx=max(mx,num[i].x);
	}
	sort(num+1,num+1+n,cmp);
	int now=1;
	for(int i=1;i<=mx;i++){
		while(now<=n&&num[now].x<=i){
			int x=num[now].id;
			t1++,t2++;ins[x]=1;f[x]=x;sz[x]=1;
			if(ins[x-1]){
				int v=fd(x-1);
				t1-=1ll*sz[v]*sz[v]+1ll*sz[x]*sz[x];
				sz[v]+=sz[x];
				t1+=1ll*sz[v]*sz[v];
				f[x]=v;
				t2--;
			}
			if(ins[x+1]){
				int v=fd(x+1);
				x=fd(x);
				t1-=1ll*sz[v]*sz[v]+1ll*sz[x]*sz[x];
				sz[v]+=sz[x];
				t1+=1ll*sz[v]*sz[v];
				f[x]=v;
				t2--;
			}
			now++;
		}
		sum[i]=t1;
		if(sum[pos[t2][0]]*i<=t1*pos[t2][0]){
			pos[t2][0]=i;
		}
	}
	for(int i=1;i<=22;i++){
		for(int j=1;j+(1<<(i-1))<=n;j++){
			if(check(j,j+(1<<(i-1)),i-1)){
				pos[j][i]=pos[j][i-1];
			}
			else{
				pos[j][i]=pos[j+(1<<(i-1))][i-1];
			}
		}
	}
	for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
	for(int i=1;i<=q;i++){
		scanf("%d%d%d%d",&a,&b,&x,&y);
		a%=n,b%=n,x%=n,y%=n;
		int l=(1ll*a*ans%n+x+n-1)%n+1;
		int r=(1ll*b*ans%n+y+n-1)%n+1;
		if(l>r) swap(l,r);
		int k=lg[r-l+1];
		long long tmp,x;
		if(check(l,r-(1<<k)+1,k)){
			tmp=sum[pos[l][k]];
			x=pos[l][k];
		}
		else{
			tmp=sum[pos[r-(1<<k)+1][k]];
			x=pos[r-(1<<k)+1][k];
		}
		if(tmp==0){
			printf("-1 -1\n");
			printf("%d %d %lld\n",l,r,ans);
			ans=1;
		}
		else{
			printf("%lld %lld\n",tmp,x);
			printf("%d %d %lld\n",l,r,ans);
			ans=1ll*tmp%n*x%n;
		}
	}
	return 0;
}
帮我调试这份代码并把错误点标记出来#include<bits/stdc++.h> #define int long long using namespace std; const int MAXN = 2e5 + 5; const int MOD = 1e9 + 7; int N, M; int jc[MAXN], inv[MAXN], zys[100], cnt[100]; int qpow( int a, int b ){ int base = a, ans = 1; while( b > 0 ){ if( b & 1 ){ ans *= base; ans %= MOD; } base *= base; base %= MOD; b >>= 1; } return ans; } void jiec(){ jc[0] = jc[1] = inv[0] = inv[1] = 1;//初始化 for( int i = 2; i <= N; i ++ ){ jc[i] = 1LL * jc[i - 1] * i % MOD; // inv[i] = 1LL * ( MOD - MOD / i ) * inv[MOD % i] % MOD; } inv[0] = 1; inv[N] = qpow(jc[N], MOD - 2); // 费马小定理求逆元 for (int i = N - 1; i >= 1; i--) { inv[i] = 1LL * inv[i + 1] * (i + 1) % MOD; } // for( int i = 1; i <= N; i ++ ) inv[i] = 1LL * inv[i - 1] * inv[i] % MOD; } int C( int n, int m ){ if( n < m ) return 0; return jc[n] * inv[m] % MOD * inv[n - m] % MOD; } signed main(){ cin >> N >> M; jiec(); int pos = 0; for( int i = 2; i * i <= M; i ++ ){ if( M % i == 0 ){ zys[++ pos] = i; cnt[pos] = 0; while( M % i == 0 ){ cnt[pos] ; M /= i; } } } if( M > 1 ){ zys[ pos] = M; cnt[pos] = 1; } int ans = 1; for( int i = 1; i <= pos; i ++ ){ ans = ans * C( N + cnt[i] - 1, cnt[i] ) % MOD; } cout << ans; return 0; }题面如下# AT_abc110_d [ABC110D] Factorization 题目描述 正整数 N , M N, M が与えられます。 a 1 × a 2 × . . . × a N = M a 1 ​ × a 2 ​ × ... × a N ​ = M となる正整数からなる長さ N N 数列 a a が何通りあるかを 10 9 + 7 10 9 +7 で割った余りを求めてください。 ただし、数列 a ′ a ′ と a ′ ′ a ′′ が異なるとは、ある i i が存在して a i ′ ≠ a i ′ ′ a i ′ ​  = a i ′′ ​ であることをいいます。 输入格式 入力は以下形式で標準入力から与えられる。 N N M M 输出格式 条件を満たす正整数からなる数列が何通りあるかを 10 9 + 7 10 9 + 7 で割った余りを出力せよ。 输入输出样例 #1 输入 #1 2 6 输出 #1 4 输入输出样例 #2 输入 #2 3 12 输出 #2 18 输入输出样例 #3 输入 #3 100000 1000000000 输出 #3 957870001 说明/提示 制約 入力はすべて整数である 1 ≤ N ≤ 10 5 1 ≤ N ≤ 10 5 1 ≤ M ≤ 10 9 1 ≤ M ≤ 10 9 Sample Explanation 1 { a 1 , a 2 } = { 1 , 6 } , { 2 , 3 } , { 3 , 2 } , { 6 , 1 } {a 1 ​ , a 2 ​ } = {1, 6}, {2, 3}, {3, 2}, {6, 1} 4 4 通り数列が条件を満たします。不要改变变量名和结构
07-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值