- 看我博客我有沒看懂的地方,或者其他疑問,可以加我qq和我交流~我會及時解答
- qq:1244536605 (加好友時備註一下 博客 )
標籤
- 難想的暴力。
簡明題意
- D題沒啥思路,放棄比賽直接開始寫博客哈哈哈哈~
- 給定n長的數組a[]
- (接下來的文章中,我把連續子序列稱爲子串,大家注意一下哈)
- 題目定義了一個數組什麼時候是good的。題目是這樣定義的:一個數組b[],如果它是由a[]數組刪掉頭部幾個元素,再從尾部刪掉幾個元素得到的,且b[]數組不包含一個和爲0的子串,那麼b[]是good的。
- 題目定義得很繞。。什麼頭部刪幾個,尾部刪幾個,,這都是誤導的信息。實際上就是說,如果一個數組是good的,那麼這個數組需要是a[]的一個子串,且這個數組本身不包含和爲0的子串。題目中,頭部刪幾個,尾部刪幾個,我開始還想了半天這是啥…後來才明白其實就是告訴你它是a[]的一個子串。
- 現在要求a[]有多少個子串是good的。
思路
- 包含a[i]的子串一共有多少個?
- 顯然是n個。從a[i]往左邊延伸得到i-1個,從a[i]往右延伸是n-i個,加上自己,就是n個。
- 那麼我們可以枚舉每一個a[i],求a[i]往左邊可以延伸出多少個good的子串,再把對於每個a[i]求得的值加起來即可。
- 下面畫一個圖,藍色部分表示我們正在考慮的a[i]。假設紅色和棕色標出的區間和爲0.相當於現在我們固定了子串的右端點,想要求出有多少個左端點可選,使得這個子串good。
- 那麼很顯然,左端點不可能在的前面。那麼是不是相當於我們在枚舉a[i]的時候要維護,和爲0的區間的最大左端點?是的。
- 假設我們維護的這個最大左端點是max_l,那麼對於每一個a[i],我們讓ans += i-max_l就好了。
- 嗯…我好像講完了,就是這麼簡單,維護就好了。
---------------------------------------分割線-----------------------------------
- 可能難點在於怎麼維護。我說說我的思路。我一看到區間和爲0,我想到了前綴和,區間和爲0等價於。那麼我們可以先前綴和一下,然後考慮每一個a[i]的時候,直接找距離i最近的j且sum[j]=sum[i]。就能找到這個和爲0的區間了,然後用這個更新max_l,再用max_l更新答案即可。
注意事項
- 無
總結
AC代碼
#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<map>
#include<queue>
#include<cstdio>
#include<set>
#include<map>
#include<string>
using namespace std;
const int maxn = 2e5 + 10;
long long dp[maxn];
map<long long, int> rec;//維護每個前綴和最近一次出現的位置
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> dp[i], dp[i] += dp[i - 1];
rec[dp[i]] = -1;
}
long long ans = 0;
int max_l = -1;
rec[0] = 0;
for (int i = 1; i <= n; i++)
{
if (rec[dp[i]] != -1)
max_l = max(max_l, rec[dp[i]]);
rec[dp[i]] = i;
ans += i - (max_l + 1);//max_l表示前綴和相等的下標,而max_l+1纔是實際的區間
}
cout << ans;
}
int main()
{
//freopen("Testin.txt", "r", stdin);
solve();
return 0;
}