luogu P4062 [Code+#1]Yazid 的新生舞會(線段樹+套路)
阿新 • • 發佈:2018-12-25
ans 線段 在線 連續 cstring 枚舉 lazy long long getchar
然後我們在線段樹上維護\(i\)和\(cnt[i]\),然後用線段樹可以把這個-1序列\(logn\)處理掉。
然後每一個1單獨處理,最後總復雜度\(O(nlogn)\)
今天原來是平安夜啊
感覺這題是道好題。
一個套路枚舉權值\(x\),把權值等於\(x\)的設為1,不等於的設為-1,然後問題轉化為多少個區間權值和大於。
發現並不是很好做,還有一個套路,用前綴和查分來表示區間。然後就是
\[i<j\]
\[sum[i]<sum[j]\]
然後樹狀數組可以做\(a[i]\leq7\)的數據。
那麽\(a[i]\)那麽大該怎麽辦?
考慮我們構建的\(1,-1\)數列中連續-1的數列很多。
然後這些連續-1不會互相影響的貢獻,然後我們考慮直接算出這些連續-1的貢獻。
設這段連續-1的兩端的sum為\(l,r\)。貢獻為:
\[\sum_{i=l}^r\sum_{j=-inf}^{i-1}cnt[j]=(r-l+1)*\sum_{i=-inf}^{l-1}cnt[i]+\sum_{i=l}^r(r-i)*cnt[i]\]
然後我們在線段樹上維護\(i\)和\(cnt[i]\),然後用線段樹可以把這個-1序列\(logn\)處理掉。
然後每一個1單獨處理,最後總復雜度\(O(nlogn)\)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> using namespace std; #define int long long #define mid ((l+r)>>1) #define ls now<<1 #define rs now<<1|1 const int N=501000; vector<int> vec[N]; int sum[N*2*5],sumi[N*2*5],lazy[N*2*5],a[N],b[N],last,lastsum,ans,n; int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return sum*f; } void update(int now){ sum[now]=sum[ls]+sum[rs]; sumi[now]=sumi[ls]+sumi[rs]; } void pushdown(int l,int r,int now){ if(lazy[now]==0)return; if(l==r)return; sum[ls]+=(mid-l+1)*lazy[now]; sumi[ls]+=(mid+l)*(mid-l+1)/2*lazy[now]; sum[rs]+=(r-(mid+1)+1)*lazy[now]; sumi[rs]+=(r+mid+1)*(r-(mid+1)+1)/2*lazy[now]; lazy[ls]+=lazy[now]; lazy[rs]+=lazy[now]; lazy[now]=0; } void add(int l,int r,int L,int R,int k,int now){ if(R<l||L>r)return; pushdown(l,r,now); if(L<=l&&r<=R){ sum[now]+=(r-l+1)*k; sumi[now]+=(l+r)*(r-l+1)/2*k; lazy[now]+=k; return; } add(l,mid,L,R,k,ls); add(mid+1,r,L,R,k,rs); update(now); } int check(int l,int r,int L,int R,int now){ if(R<l||L>r)return 0; pushdown(l,r,now); if(L<=l&&r<=R)return sum[now]; return check(l,mid,L,R,ls)+check(mid+1,r,L,R,rs); } int checki(int l,int r,int L,int R,int now){ if(R<l||L>r)return 0; pushdown(l,r,now); if(L<=l&&r<=R)return sumi[now]; return checki(l,mid,L,R,ls)+checki(mid+1,r,L,R,rs); } signed main(){ n=read();int hh=read(); for(int i=1;i<=n;i++)a[i]=read(),b[i]=a[i]; sort(b+1,b+1+n); int tot=unique(b+1,b+1+n)-b-1; for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+tot,a[i])-b; for(int i=1;i<=n;i++)vec[a[i]].push_back(i); add(1,2*n,n,n,1,1); for(int i=1;i<=tot;i++){ vec[i].push_back(n+1); last=0;lastsum=0; for(int j=0;j<vec[i].size();j++){ int L=vec[i][j]-last-1; int l=lastsum-L; int r=lastsum-1; if(vec[i][j]!=last+1){ ans+=L*check(1,2*n,1,l+n-1,1); ans+=(r+n)*check(1,2*n,l+n,r+n,1); ans-=checki(1,2*n,l+n,r+n,1); add(1,2*n,l+n,r+n,1,1); } if(vec[i][j]==n+1)continue; ans+=check(1,2*n,1,l+n,1); add(1,2*n,l+1+n,l+1+n,1,1); last=vec[i][j];lastsum=l+1; } last=0;lastsum=0; for(int j=0;j<vec[i].size();j++){ int L=vec[i][j]-last-1; int l=lastsum-L; int r=lastsum-1; if(vec[i][j]!=last+1)add(1,2*n,l+n,r+n,-1,1); if(vec[i][j]==n+1)continue; add(1,2*n,l+1+n,l+1+n,-1,1); last=vec[i][j];lastsum=l+1; } } printf("%lld\n",ans); return 0; }
luogu P4062 [Code+#1]Yazid 的新生舞會(線段樹+套路)