codeforces 718E. Matvey's Birthday
題目連結:http://codeforces.com/contest/718/problem/E
題目大意:給定一個長度為n的只包含a~h的字串s,對於1<=a,b<=n,,如果|a-b|=1或s[a]=s[b],就在a與b之間連一條無向邊,求這個圖的直徑以及有多少對點的距離(這裡指最短距離)等於這個直徑。直徑即一個圖中距離最大的兩個點之間的距離。
資料範圍:2<=n<=100 000
題解:設 f [ i ][ c ] 表示從點 i 走到字母為 c 的點的最短距離,這個可以通過列舉 c 然後 bfs 得到,時間複雜度O (n*sigma) 或 O (n*sigma^2) 。對於兩個點 i , j ,它們的最短路有兩種情況:
1.最短路上只經過相鄰點的連邊.
2.最短路上有經過同種字母之間的連邊.
於是我們可以知道i和j的距離等於min(|i - j| , min(f[i][c]+1+f[j][c]))。
再設dist[c1][c2]為字母為c1和c2的點的最小距離,顯然dist[s[i]][c]<=f[i][c]<=dist[s[i]][c]+1,所以我們計算每個點的狀態mark[ i ],其中mark[ i ][c]=f[i][c]-dist[s[i]][c],於是我們就可以從點和點的距離(通過mark)變成點和字母的距離啦,至於為什麼不是字母和字母的距離,那是因為這樣就不能排除情況1的最短路了。
到這裡解決方法就差不多出來了,我們從1到n列舉點 i ,然後算出在 i 之前的哪些點與 i 距離屬於情況1,這樣的點一定是連續的,假設是now~i,然後我們統計1~now-1中的兩個值,其中num[x][y]=字母為 x 狀態為 z(z包含y,即y&z=y) 的點的個數之和,nums[x]=字母為x的點的個數,時間複雜度O(n*2^sigma)
然後列舉點 i 到字母為 j 的點的距離dst(顯然dst=min(f[i][c]+1+dist[c][j]+字母為 j 的點的mark值的第c位))和個數cnt,時間複雜度O(n*sigma^2),
令mindst=min(f[i][c]+1+dist[c][j]),mrk表示狀態(其中若f[i][c]+1+dist[c][j]=mindst則mrk[c]=1否則mrk[c]=0),若num[j][mrk]>0那麼dst=mindst+1,cnt=num[j][mrk],否則dst=mindst,cnt=nums[j]。然後用dst和cnt更新答案。
最後不要忘了還要判斷一下 i 到now的距離。
程式碼如下:
#include <algorithm>
#include <cstdio>
using namespace std;
const int N=100005;
int d[N],q[N],fi[10],ne[N],f[N][10],dist[10][10],mark[N],
num[10][1<<8],nums[10],n;
char s[N];
void bfs(int c){
int h=1,t=0,u[10];
for (int i=1;i<=n;i++)
if (s[i]-'a'==c) d[q[++t]=i]=0;else d[i]=-1;
for (int i=0;i<8;i++) u[i]=1;u[c]=0;
for (;h<=t;h++){
if (u[s[q[h]]-'a']){
u[s[q[h]]-'a']=0;
for (int i=fi[s[q[h]]-'a'];i;i=ne[i])
if (d[i]==-1) d[q[++t]=i]=d[q[h]]+1;
}
if (q[h]>1 && d[q[h]-1]==-1) d[q[++t]=q[h]-1]=d[q[h]]+1;
if (q[h]<n && d[q[h]+1]==-1) d[q[++t]=q[h]+1]=d[q[h]]+1;
}
for (int i=1;i<=n;i++) f[i][c]=d[i];
}
int main(){
scanf("%d\n%s",&n,s+1);
for (int i=1;i<=n;i++){
ne[i]=fi[s[i]-'a'];fi[s[i]-'a']=i;
}
for (int i=0;i<8;i++) bfs(i);
for (int i=0;i<8;i++)
for (int j=0;j<8;j++) dist[i][j]=-1;
for (int i=1,j;i<=n;i++)
for (j=0;j<8;j++)
if (f[i][j]!=-1 && (dist[s[i]-'a'][j]==-1 || f[i][j]<dist[s[i]-'a'][j]))
dist[s[i]-'a'][j]=f[i][j];
for (int i=1,j;i<=n;i++){
mark[i]=0;
for (j=7;j>=0;j--) mark[i]=mark[i]<<1^(f[i][j]-dist[s[i]-'a'][j]);
}
int ans=0,now=1;long long ansp=0;
for (int i=1,j,k;i<=n;i++){
int l=i;
for (;l>=1;l--){
int mindst=-1;
for (j=0;j<8;j++)
if (f[l][j]!=-1 && f[i][j]!=-1 && (mindst==-1 || f[l][j]+f[i][j]+1<mindst))
mindst=f[l][j]+f[i][j]+1;
if (mindst<=i-l) break;
}
for (;now<=l;now++){
nums[s[now]-'a']++;
for (j=mark[now];j;j=(j-1)&mark[now]) num[s[now]-'a'][j]++;
}
for (j=0;j<8;j++){
int mindst=-1,minnum=0;
for (k=0;k<8;k++)
if (f[i][k]!=-1 && dist[k][j]!=-1 && (mindst==-1 || f[i][k]+dist[k][j]+1<mindst))
mindst=f[i][k]+dist[k][j]+1;
if (mindst==-1) continue;
int mrk=0;
for (k=0;k<8;k++)
if (f[i][k]!=-1 && dist[k][j]!=-1 && f[i][k]+dist[k][j]+1==mindst) mrk^=1<<k;
if (num[j][mrk]) mindst++,minnum=num[j][mrk];
else minnum=nums[j];
if (!minnum) continue;
if (mindst>ans) ans=mindst,ansp=minnum;
else if (mindst==ans) ansp=ansp+minnum;
}
if (i-now>ans) ans=i-now,ansp=1;
else if (i-now==ans) ansp++;
}
printf("%d %I64d\n",ans,ansp);
}
--------------------- 本文來自 aufeas 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/aufeas/article/details/52895953?utm_source=copy