對於乙個字串 \(s\),我們定義 \(|s|\) 表示 \(s\) 的長度。
接著,我們定義 \(s_i\) 表示 \(s\) 中第 \(i\) 個字元,\(s_\) 表示由 \(s\) 中從左往右數,第 \(l\) 個字元到第 \(r\) 個字元依次連線形成的字串。特別的,如果 \(l > r\) ,或者 \(l < [1, |s|]\), 或者 \(r < [1, |s|]\) 我們可以認為 \(s_\) 為空串。
給定乙個長度為 \(n\) 的僅由數字構成的字串 \(s\),現在有 \(q\) 次詢問,第 \(k\) 次詢問會給出 \(s\) 的乙個字串 \(s_\) ,請你求出有多少對 \((i, j)\),滿足 \(1 \le i < j \le n\),\(i + 1 \lt j\),且 \(s_\) 出現在 \(s_\) 中或 \(s_\) 中或 \(s_\) 中。
對於所有測試資料,\(1 \le n \le 10^5\),\(1 \le q \le 3 · 10^5\),\(1 \le l \le r \le n\)。
大概思路就是正難則反、分類討論。
將原問題轉化為被分成的3部分都不含蓋子串的方案數。
可以將分割看成選原串之間的縫隙切2刀。
有可能第一刀就已經滿足條件了,此時要求切到子串最左位置和最右位置。第二刀隨便切在右邊就行了。
有可能第一刀沒有切掉任何乙個子串,第二刀切完。此時要求第一刀切在第乙個子串左邊。
有可能第一刀切了一些,第二刀切了剩下的。設第一刀切了前\(i\)個串,即剛好沒有切掉第i+1個串。
令\(l_i,r_i\)為子串第\(i\)次出現的左右端點,顯然有\(r_i=l_i+len-1\),\(len\)為串長。
再令\(p_2=r_m-len+1,p_1=r_1+len-1\),其中m為子串出現次數。
觀察第一刀、第二刀滿足的條件,為:\(l_il_m\)。
化簡,得\(r_>p_2,r_i。
另外顯然\(r_>r_i\)
於是我們得到了大小關係的幾種情況:
\(r_
\(r_
\(r_
$p_ \leq r_$p_\(r_
\(r_=p_
\(r_
除了第4種情況,顯然其他情況最多只有乙個\(i\)滿足條件。
然後考慮每個\(i\)對答案的貢獻:\((l_-l_)*(r_-l_)\)
但是這個式子過不了樣例。。。
觀察一下樣例的第乙個詢問,發現第一刀的範圍不一定是\((l_-l_)\),有可能\(r_\)在\(l_\)到\(l_\)。
所以式子變為
\(min(l_-l_,r_1-l_i)*(r_-l_)=min(r_-r_,p_1-r_i)*(r_-l_)\)
考慮怎麼求值。應該知道要用線段樹求出right集合了吧
除了第4種情況以外,其他情況最多隻需要求前驅、後繼就行了。
第四種情況發現min是可以去掉的(雖然我的**智障地全都加了),於是\(\sum (r_-r_)*(r_-l_)=\sum (r_-r_)*r_- \sum l_*(r_-r_)=\sum (r_-r_)*r_-l_m*(r_y-r_x)\),其中\(x,y\)分別為最前、最後乙個滿足條件的串。於是我們需要多維護乙個\(\sum (r_-r_)*r_\),發現這個東西在存了區間最大最小值後,可以左右區間合併。
然後就沒有啦。具體細節(如倍增定位子串位置)見辣雞**
#include #include #include #include #include using namespace std;
const int q=1<<19,p=1<<20;
#define ll long long
int ch[p][10],mx[p],par[p];
int tot=0;
int push(int val)
int rt=push(0),lp=rt;
int sgn[p];
int include_today_gg=0;
void sam(int v)
}sgn[np]=++include_today_gg;
lp=np;
}struct graph
}tr,qu;
const int s=1<<20;
int pool[s],toap=0;
struct dtw[s];
dt operator+(dt a,dt b)
;}int ls[s],rs[s];
void upd(int x)
int tl=0;
int new()
void merge(int &x,int y,int l,int r)
int mid=(l+r)>>1;
merge(ls[x],ls[y],l,mid);
merge(rs[x],rs[y],mid+1,r);
upd(x);
pool[++toap]=y;
}void mdf(int &x,int l,int r,int owo)
int mid=(l+r)>>1;
if(owo<=mid)mdf(ls[x],l,mid,owo);
else mdf(rs[x],mid+1,r,owo);
upd(x);
}dt gans(int now,int l,int r,int x,int y)
int qian(int now,int l,int r,int x)
int n;
ll ans[q];
int rt[p];
#define qian_(owo) qian(rt[x],1,n,owo)
#define hou_(owo) hou(rt[x],1,n,owo)
ll c2(int x)
ll gg(int x,int len)
if(p1=1&&hou_(t1)==t2)als+=1ll*min(t2-t1,p1-t1)*(t2-ln);
}if(p2=1&&hou_(t1)==t2)als+=1ll*min(t2-t1,p1-t1)*(t2-ln);
}if(p2p2&&o=1&&(o=qian_(t2))p2)als+=1ll*min(t2-o,p1-o)*(t2-ln);
if(t1<=n&&t2>=1&&t1==t2)
int oo;
if(hou_(p2-1)==p2)
if(qian_(p1+1)==p1)
dfs(1);
for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
return 0;
}
p.s.:感謝chenjingqi大佬幫忙找錯 八省聯考2018 制胡竄
首先,本題parent tree上樹上倍增 線段樹合併找出每個點的 text 集合應該是沒得說的。於是我們現在考慮知道了 text 集合以及詢問串長度 len 怎麼求出答案。首先,乙個正常人稍微想想,都應該得出正難則反的推論,因為無論怎麼說不合法的劃分明顯要來得比較工整。於是問題轉化為割兩刀,能否切...
九省聯考 2018
發現狀態數很少,直接搜即可。不難發現這個偏序關係形成了一棵樹。本來以為直接貪心即可,即把 a 排序,然後 dfs bfs 一遍直接安排權值,類似於這樣 void dfs1 int u void dfs2 int u 不出我所料,這份簡單的 沒有過,被這組資料叉掉了 2 2.0 1 1 1 2發現這樣...
九省聯考2018遊記
day0 上午到學校,去tututu寢室,入坑荒野求生,真tm好玩 雖然只打了一把 下午坐高鐵去sh,發現sh非正式選手只有hez的。day1 開局看t1,沒意識到狀態數是個組合數,於是寫了個程式算狀態數,還寫掛了 include int n 10,m 10,f 15 15 i,j,k int ma...