说实话这种区间计数问题自己又大脑短路了,以后再也不能犯这种问题了(TAT)
原题连接:http://codeforces.com/contest/1333/problem/C
题意:求判定为“good”的子区间个数。good : 在该区间内的所有子区间,不存在和为0的子区间。子序列:对原序列首位依次删除任意个形成的序列
思路:从对 “所有子区间” 的统计符合条件个数可以看出,这是一道标准的区间计数问题。而对于区间计数的方式由于是对所有区间的统计,所以一定会用到 贡献的思想。
思考1:如何覆盖所有区间通过 for 循环
方案一:以 i 为起点 j 为终点,二维数组来表示区间状态 (一些dp的表现方式 , 或者以 i为起点,j为当前位置向后长度)
方案二:以 i 为起点 j 为向前长度的位置 , 如下图所示的覆盖方式 (Codeforces Round #585 (Div. 2) B. The Number of Products :也是用的同样的方法
而这样的方法有一个好处,就是 通过前缀和 map 来判断 在 map[a[i]] ~ i 这个区间范围的和为0 . 同时这一方法在 统计所有异或值为0的子区间,也是用同样的方法。
还是今年牛客寒假里面的一道题,自己 AC 过(TAT), 所以通过map维护一个前缀和为 x 的最后位置,以 i 为右端点, 则在区间 map[a[i]]+1 ~ i 这里必定没有非零子区间(前提是没有0包含)
所以嗯,对应的 code 就这样出来了: 这里由于 下标的问题,我把原数组下标全部向右移动了一格
#include<bits/stdc++.h> #define IOS ios::sync_with_stdio(0); cin.tie(0); typedef long long ll; using namespace std; const int maxn = 1e6+5; ll a[maxn]; map<ll,ll>mp; int main(){ IOS int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=2;i<=n;i++) a[i] += a[i-1]; // 前缀和 for(int i=n+1;i>=2;i--) a[i] = a[i-1]; a[1] = 0; ll ans = 0,p = 1; ll i; mp.clear(); mp[0] = 1; for(i=2;i<=n+1;i++){ if(mp[a[i]]) p = max(p,mp[a[i]]+1); mp[a[i]] = i; if(a[i]-a[i-1] == 0) { p = i; continue; } ans += i - p; //非零区间个数 } cout<<ans<<endl; }