Minimum ,Maxinum (单调栈,区间贡献处理细节)

思路

给定数组a,求满足条件的区间数

  • 区间中的最大值是最小值的倍数

constrains:
1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105

思路

先单调栈处理出 l e s s l , l e s s r less_l,less_r lessl,lessr左右第一个小, g e l , g e r ge_l,ge_r gel,ger左右第一个大,有一个细节, g e r ge_r ger必须大于a[i]自身。
首先 d i [ j ] di[j] di[j]处理出 j j j的所有因数 O ( A l o g A ) O(AlogA) O(AlogA)
我们考虑枚举最大值的贡献,假设当前最大值为 a [ i ] a[i] a[i],枚举他的因数 j = d i [ a [ i ] ] j = di[a[i]] j=di[a[i]],维护i左边第一个 j 1 j_1 j1和右边第一个 j 2 j_2 j2,可以用双指针实现,两者必须在 ( g e l [ i ] , g e r [ i ] ) (ge_l[i],ge_r[i]) (gel[i],ger[i]) 才有贡献,那么 j 1 j_1 j1的贡献为 ( l e s s l [ j 1 ] , l e s s r [ j 2 ] ) (less_{l}[j_1],less_{r}[j_2]) (lessl[j1],lessr[j2])和i的区间的交集的 j 1 j_1 j1的左开右闭和 i 的左闭右开 i的左闭右开 i的左闭右开,开闭必须想清楚, j 2 j_2 j2要避免重复贡献(因为 l e s s l [ j 2 ] less_l[j_2] lessl[j2]包含了 j 1 j_1 j1)。 g e r ge_r ger大于自身,使得 i i i不会重复贡献(非常像逆序对的贡献方式,卡掉某一个方向的贡献就能避免重复,但要想清楚不容易)。

代码

#include<bits/stdc++.h>
using namespace std;
#define pow2(X) (1ll<<(X))
#define SIZE(A) ((int)A.size())
#define LENGTH(A) ((int)A.length())
#define ALL(A) A.begin(),A.end()
#define F(i,a,b) for(ll i=a;i<=(b);++i)
#define dF(i,a,b) for(ll i=a;i>=(b);--i)
#define GETPOS(c,x) (lower_bound(ALL(c),x)-c.begin())
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
#define pb push_back
#define pr pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define eps 1e-6
#define PI acos(-1.0)
#define lb lower_bound
#define ub upper_bound
#define bs binary_search
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
#define SZ 666666
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> ipair;
typedef vector<int> VI;
typedef vector<long long> VLL;
typedef vector<vector<long long > > VVLL;
typedef vector<vector<int> > VVI;
typedef vector<double> VD;
typedef vector<string> VS;
//const int mods = 998244353;
const int mods = 1e9+7;
const int maxn = 5e5+10;
const int N = 5e5+10;
const int E = 1e4+10;
const int lim = 1e6+10;
ll qpow(ll a,ll b) {ll res=1;a%=mods; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mods;a=a*a%mods;}return res;}
ll lcm(ll a, ll b) {return a / __gcd(a, b) * b;}
int read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}

/***********************************
learning time
u=*max_element(a.begin(),a.end())
***********************************/

ll n,m,k;
int a[maxn];
int less_l[maxn],less_r[maxn],ge_l[maxn],ge_r[maxn],ind[lim];
vector<int> di[lim];
vector<int> pos[lim];

int main(){
	// freopen("C:\\Users\\Gao\\Desktop\\validation_input\\watering_well_chapter_2_input.txt","r",stdin);
	// freopen("C:\\Users\\Gao\\Desktop\\validation_input\\output.txt","w",stdout);
	ios_base::sync_with_stdio(0);

	F(i,1,lim-10){
		for(int j = i;j<=lim-10;j+=i){
			di[j].pb(i);
		}
	}

    int T;
    cin>>T; 
	//T = 1;
	F(turn,1,T){
		cin>>n;
		F(i,1,n){
			cin>>a[i];
			pos[a[i]].pb(i);
		}

		{
			stack<int> s;
			F(i,1,n){
				while(!s.empty()&&a[s.top()]>=a[i]) s.pop();
				less_l[i] = (s.empty()?0:s.top());
				s.push(i);
			}
		}
		{
			stack<int> s;
			dF(i,n,1){
				while(!s.empty()&&a[s.top()]>=a[i]) s.pop();
				less_r[i] = (s.empty()?n+1:s.top());
				s.push(i);
			}
		}
		{
			stack<int> s;
			F(i,1,n){
				while(!s.empty()&&a[s.top()]<a[i]) s.pop();
				ge_l[i] = (s.empty()?0:s.top());
				s.push(i);
			}
		}
		{
			stack<int> s;
			dF(i,n,1){
				while(!s.empty()&&a[s.top()]<=a[i]) s.pop();
				ge_r[i] = (s.empty()?n+1:s.top());
				s.push(i);
			}
		}

		ll ans = 0;
		F(i,1,n){
			for(auto j:di[a[i]]){	//pos[j] must be before and after maa,so O(n) push it go
				//while(ind[j]<pos[j].size()&&pos[j][ind[j]]<i) ind[j]++;
				if(ind[j]>0){
					int p = pos[j][ind[j]-1];
					if(i<less_r[p]&&p>ge_l[i]){
						int rig = min(less_r[p],ge_r[i]);
						int lef = max(less_l[p],ge_l[i]);
						ans += 1ll*(rig-i)*(p-lef);
					} 
				}
				if(ind[j]<pos[j].size()){
					int p = pos[j][ind[j]];
					if(i>less_l[p]&&p<ge_r[i]){
						int rig = min(less_r[p],ge_r[i]);
						int lef = max({less_l[p],ge_l[i],ind[j]>=1?pos[j][ind[j]-1]:-1});
						ans += 1ll*(rig-p)*(i-lef);
					}
				}
			}
			ind[a[i]]++;//保证a[i]恒在下一个maa的后面(或重叠)
		}
		cout<<ans<<endl;

		F(i,1,n){
			for(auto j:di[a[i]]){
				ind[j] = 0;
				pos[j].clear();
			}
			less_l[i]=less_r[i]=ge_l[i]=ge_r[i]=0;
		}
	}
}
 
/*

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值