1. 程式人生 > 其它 >Codeforces 1270 F. Awesome Substrings —— 根據資料大小使用不同方法

Codeforces 1270 F. Awesome Substrings —— 根據資料大小使用不同方法

技術標籤:想法

This way

題意:

給你一個01串,問你有多少子串,它包含的1的個數是它長度的因子。

題解:

這種題目想不出來的時候應該列一下公式啊,設sum[i]表示到了第i個位置的時候,有多少個1.
那麼就是要我們求 i − ( j − 1 ) = ( s u m [ i ] − s u m [ j − 1 ] ) ∗ x i-(j-1)=(sum[i]-sum[j-1])*x i(j1)=(sum[i]sum[j1])x這裡有三個變數:i,j,x。
i和j就表示這個子串的末尾和起始位置,x就表示長度是1個數的多少倍
然後我們可以知道 ( s u m [ i ] − s u m [ j − 1 ] ) ∗ x < = n (sum[i]-sum[j-1])*x<=n

(sum[i]sum[j1])x<=n
於是對於1的個數和x的值我們可以分開考慮。
首先是列舉1的個數的情況i,我們同時列舉左端點的位置l,然後對於左端點右邊第i個1和第i+1個1這個區間直接算出有多少種情況加到答案裡即可:
在這裡插入圖片描述
然後是列舉乘數的情況,首先要確定一點:假設我們列舉1的個數是從1列舉到m,那麼此時運算的時候1的個數一定要大於m:
在這裡插入圖片描述
繼續推上面那個公式 i − s u m [ i ] ∗ x = ( j − 1 ) − s u m [ j − 1 ] ∗ x i-sum[i]*x=(j-1)-sum[j-1]*x isum[i]x=(j1)sum[j1]x
那麼我們直接列舉右端點,檢視有多少左端點合法,我這裡偷懶了用unordered_map來記,因此這個時候m的取值必須要較大,我這裡取的是2000,也就是讓第一種情況畫的時間較多一點,第二種情況由於有個小log,所以其它時間花的要較少一點。
在這裡插入圖片描述

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int N=2e5+5;
char s[N];
vector<int>p1;
int sum[N*2];
unordered_map<int,int>num;
int main()
{
    int n;
    scanf("%s",s+1);
    n=strlen(s+1);
    int m=min(n,2000);
    for(int i=1;i<=n;i++){
        if
(s[i]=='1') p1.push_back(i); sum[i]=sum[i-1]+(s[i]=='1'); } p1.push_back(n+1); int sz=p1.size(); ll ans=0; for(int i=1;i<=m&&i<sz;i++){ int l=1,r=i; for(;l<=n;l++){ ans+=(p1[r]-l)/i-(p1[r-1]-l)/i; if(s[l]=='1')r++; if(r>=sz)break; } } if(m>=sz)return 0*printf("%lld\n",ans); for(int k=1;k<=n/m;k++){ num.clear(); int r=m,l=0,pl=1,v; num[0]=1; for(int i=p1[m];i<=n;i++){ r+=s[i]=='1'; while(sum[i]-sum[pl]>m){ l+=s[pl]=='1'; v=pl-l*k; num[v]++; pl++; } v=i-r*k; ans+=num[v]; } } printf("%lld\n",ans); return 0; }