九省聯考 2018 制胡竄

2022-05-25 13:54:10 字數 3294 閱讀 9665

對於乙個字串 \(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...