NOI2015 品酒大會

2021-10-01 09:35:04 字數 2579 閱讀 5024

字尾自動機

首先考慮第一問:

我們先將字尾自動機建出來,考慮每個節點,它出現的次數肯定是endpos的size(我們記為num),那麼選取這個節點的串的方案數即為c(num,2)=num*(num-1)/2,所能貢獻的長度區間為這個節點對應的所有串的長度即[ len[fa[x]]+1 , len[x]+1 ],這裡可以差分一下,最後字首和就是答案了。

然後考慮第二問:

我們考慮每當我們在字尾自動機上新建乙個節點,就把這個位置的a[i]給賦值上去。當然,在建的過程中由於endpos裂開而產生的的那個新的節點不賦值,因為最後因endpos裂開的而產生的新節點的nq在parent tree上一定是原節點q的father,所以最後的值會更新上去,如果賦值了,就相當於額外多了乙個值,答案當然會不對。

那麼我們再考慮在parent tree上應該怎麼做。在parent tree上,father一定是son的字尾,而對於這題來說,某兩個串是r相似的,那麼它們一定是[0,r-1]相似的,即一對r相似串的a1*a2有貢獻那麼對它們的字首一定也有貢獻,所以我們可以將串倒序建字尾自動機,這樣字尾就變成了字首,lcp就變成了lca!因為此題a有負數,所以我們要維護最大次大最小次小,那麼因為現在parent tree上每個父親都是兒子的字首,所以子樹的最優可以更新父親的最優。那麼自然的我們就可以對每個節點維護四個值(最大次大最小次小),然後每個節點的值為它即它所有節點的最後(樹形dp轉移),然後貢獻的區間顯然為[ len[fa[x]]+1 , len[x]+1 ],這個我們可以用線段樹維護一下,這是直接可以標記永久化的,也非常好寫。

效率:o(nlogn)

#include #include #include #include #include #include #define pb push_back

#define lch a[n].lc

#define rch a[n].rc

using namespace std;

const long long inf=1e18+10;

const int maxn=6e5+10;

int n;

char s[maxn];

long long vvv[maxn];

struct suda

po[maxn];

vectornow[maxn];

struct daaa[4*maxn];

int las,tot,si[maxn],to[maxn],nt[maxn],st[maxn],topt,root,cnt;

long long v[maxn],sum[maxn],ansv[maxn],ma[maxn],cima[maxn],mi[maxn],cimi[maxn];

void init() }

void insert(int x,long long val)

}} void add(int x,int y)

void build_tree(int &n,int l,int r)

void tree_max(int n,int l,int r,int l,int r,long long kk)

int mid=(l+r)>>1;

if (r<=mid) tree_max(lch,l,mid,l,r,kk);

else if (l>=mid+1) tree_max(rch,mid+1,r,l,r,kk);

else tree_max(lch,l,mid,l,mid,kk),tree_max(rch,mid+1,r,mid+1,r,kk);

} void qury(int n,int l,int r,int lc)

void getsi(int x)

else if (ma[to[p]]>cima[x]) cima[x]=ma[to[p]];

if (cima[to[p]]>=ma[x])

else if (cima[to[p]]>cima[x]) cima[x]=cima[to[p]];

if (mi[to[p]]<=mi[x])

else if (mi[to[p]]=2 && x!=1)

//printf("kkqq %d %d\n",x,si[x]);

//if (si[x]) now[si[x]].pb(po[x].vv),printf("kkqqkkqq %d si[x]=%d %lld\n",x,si[x],po[x].vv);

} void solve()

build_tree(root,1,n); getsi(1);

//qury(root,1,n,1); printf("cknow %lld\n",ansv[1]);

for (int i=1;i<=n;i++) sum[i]=sum[i-1]+v[i];

sum[0]=v[0];

//for (int i=1;i<=tot;i++)

//printf("kkkqqq (%d,%d) 1=%lld 2=%lld 3=%lld 4=%lld\n",po[po[i].fa].len+1,po[i].len,ma[i],cima[i],mi[i],cimi[i]);

for (int i=0;i}

}sam;

int main()

Noi 2015 品酒大會

題目等價於求任意兩對字尾的lcp的值小於等於1,2 n的個數,以及權值乘積的最大值。求出字尾陣列的height值,然後預處理出每個height值能夠成為最小的區間。考慮每個height的值對答案的貢獻 如果height i 能夠成為最小的區間為 l,r 那麼個數便是 l i 1 r i 1 而乘積最...

NOI2015 品酒大會

一年一度的 幻影閣夏日品酒大會 隆重開幕了。大會包含品嚐和趣味挑戰 兩個環節,分別向優勝者頒發 首席品酒家 和 首席獵手 兩個獎項,吸引了眾多品酒師參加。在大會的晚餐上,調酒師 rainbow 調製了 n 杯雞尾酒。這 n 杯雞尾酒排成一行,其中第 n 杯酒 1 i n 被貼上了乙個標籤si,每個標...

NOI2015 品酒大會

一年一度的 幻影閣夏日品酒大會 隆重開幕了。大會包含品嚐和趣味挑戰 兩個環節,分別向優勝者頒發 首席品酒家 和 首席獵手 兩個獎項,吸引了眾多品酒師參加。在大會的晚餐上,調酒師 rainbow 調製了 n 杯雞尾酒。這 n 杯雞尾酒排成一行,其中第 n 杯酒 1 i n 被貼上了乙個標籤si,每個標...