【codeforces 718E】E. Matvey's Birthday

题目大意&链接:

  http://codeforces.com/problemset/problem/718/E

  给一个长为n(n<=100 000)的只包含‘a’~‘h’8个字符的字符串s。两个位置i,j(i!=j)存在一条边,当且仅当|i-j|==1或s[i]==s[j]。求这个无向图的直径,以及直径数量。

题解:

   命题1:任意位置之间距离不会大于15。

  证明:对于任意两个位置i,j之间,其所经过每种字符不会超过2个(因为相同字符会连边),所以i,j经过节点至多为16,也就意味着边数至多为15。

  然后我们对于每个节点,需要计算出其与其他节点距离,当然为了不重复,只需考虑pos小于当前节点的位置。此时我们分为两种情况:

  1.|i-j|<=15;

  2.|i-j|>15。

  对于第一种我们是取|i-j|与i先到达一种字母,j也到达这种字母距离和取较小值。所以我们设

  $F[i][c]$表示i节点到达c这种字母的最小距离。即i到j的距离为:$min(|i-j|,F[i][c]+1+F[j][c])$。

  命题2:i与j到达的同一种字母的位置如果相同,一定不是最优解。

  证明:假设到达同一个位置,那么只可能是通过这个位置的左右两个节点。那么,对于到达左右这两个节点如果其路径之间不存在相同的字母,那么其距离和|i-j|相同,如果存在相同字母,则一定比当前方式短。综上所述,到达同一位置一定不是最短路。

  对于第二种,我们由命题1可知,其距离不会大于15。我们再来看一个命题:

  命题3:设$dis[c1][c2]$表示c1字母到达c2字母的最小距离,那么我们有,若$s[i]==c1$,则$dis[c1][c2]<=F[i][c2]<=dis[c1][c2]+1$。

  证明:这个……显然吧?

  我们此时考虑对于第二种情况下的j,|i-j|一定不是最短的,所以一定是从$F[i][c]+1+F[j][c]$中选取最小值,那么由命题3可知,我们并不需要其确切位置,仅需知道$F[i][c]$与$dis[ci][c]$之间的关系,然后我们可以用一个二进制数$mark[j]$来表示其与$dis[cj][c]$之间的关系,然后我们把关系相同(即mark[j]相同)的j统计其数量,然后再求一下此时i与某一种mark之间的最短路即可。

代码:

  

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 const int N=100100;
 6 int n,f[N][16],dis[20][20];
 7 char s[N];
 8 int q[N],d[N];
 9 int mark[N];
10 int c[N][1<<8];
11 inline void bfs(int c){
12     int l=0,r=0;
13     for(int i=1;i<=n;i++)if(s[i]-‘a‘==c){
14         q[r++]=i,d[i]=0;
15     }else   d[i]=-1;
16     bool vis[16]={0};
17     vis[c]=true;
18     while(l<r){
19         int now=q[l++];
20         if(!vis[s[now]-‘a‘]){
21             vis[s[now]-‘a‘]=true;
22             for(int i=1;i<=n;i++)
23                 if(s[i]==s[now]&&d[i]==-1){
24                     d[i]=d[now]+1;
25                     q[r++]=i;
26                 }
27         }
28         if(now>1&&d[now-1]==-1) q[r++]=now-1,d[now-1]=d[now]+1;
29         if(now<n&&d[now+1]==-1) q[r++]=now+1,d[now+1]=d[now]+1;
30     }
31     for(int i=1;i<=n;i++)
32         if(d[i]!=-1)f[i][c]=d[i];
33 }
34 int main(){
35    // freopen("1.out","w",stdout);
36     scanf("%d",&n);
37     scanf("%s",s+1);
38     memset(f,0x3f,sizeof(f));
39     for(int i=0;i<8;i++)
40         bfs(i);
41     memset(dis,0x3f,sizeof(dis));
42     for(int i=1;i<=n;i++)
43         for(int j=0;j<8;j++)
44             dis[s[i]-‘a‘][j]=min(dis[s[i]-‘a‘][j],f[i][j]);
45     for(int i=1;i<=n;i++)   for(int j=0;j<8;j++)
46         if(f[i][j]>dis[s[i]-‘a‘][j])    mark[i]|=1<<j;
47    // for(int i=1;i<=n;i++)
48    //     printf("mark[%d]=%d\n",i,mark[i]);
49     int ans=0;
50     long long cnt=0;
51     for(int i=1;i<=n;i++){
52         for(int j=max(i-15,1);j<i;j++){
53             int now=i-j;
54             for(int k=0;k<8;k++)
55                 now=min(now,f[j][k]+1+f[i][k]);
56             if(now==ans)    cnt++;
57             if(now>ans)     ans=now,cnt=1;
58         }
59         int t=i-16;
60         if(t>=1)    c[s[t]-‘a‘][mark[t]]++;
61         for(int j=0;j<8;j++)    for(int k=0;k<256;k++)
62             if(c[j][k]){
63                 int now=0x7fffffff;
64                 for(int l=0;l<8;l++){
65                     now=min(now,dis[j][l]+1+f[i][l]+((k&(1<<l))>>l));
66                    //printf("%d\n",(k&(1<<l))>>l);
67                 }
68                 if(now==ans)    cnt+=c[j][k];
69                 if(now>ans) ans=now,cnt=c[j][k];
70             }
71     }
72     printf("%d %lld\n",ans,cnt);
73 }

【codeforces 718E】E. Matvey's Birthday

时间: 2024-12-21 21:58:39

【codeforces 718E】E. Matvey's Birthday的相关文章

【codeforces 415D】Mashmokh and ACM(普通dp)

[codeforces 415D]Mashmokh and ACM 题意:美丽数列定义:对于数列中的每一个i都满足:arr[i+1]%arr[i]==0 输入n,k(1<=n,k<=2000),问满足[数列长度是k && 数列中每一个元素arr[i]在1~n之间 && 数列中元素可以重复]的数列有多少个?结果对10^9+7取余 解题思路:dp[i][j]表示长度是j,最后一位是i的种数 if(kk%i==0) dp[kk][j+1]+=dp[i][j] 1 #i

【Codeforces 368A】Brain&#39;s Photos 水题

黑白灰都是#Black&White #include <cstdio> int n,m; int main() { scanf("%d%d",&n,&m); int ok=0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { char s[5]; scanf("%s",s); if(s[0]!='W'&&s[0]!='B'&&s[0]!='G')

【Codeforces 1114C】Trailing Loves (or L&#39;oeufs?)

[链接] 我是链接,点我呀:) [题意] 问你n!的b进制下末尾的0的个数 [题解] 证明:https://blog.csdn.net/qq_40679299/article/details/81167283 这题的话m比较大, 做个质因数分解就ok>_< 算n!有多少个x因子的话 以5为例子 (n=25) 25 20 15 10 5 把他们都除5 5 4 3 2 1 然后再除5 1 所以总共有6个 转换成代码就是 while(n>0){ ans+=n/5; n = n/5; } [代码

【Codeforces 332C】Students&#39; Revenge

Codeforces 332 C 我爱对拍,对拍使我快乐... 题意:有\(n\)个议题,学生们会让议会同意\(p\)个,其中主席会执行\(k\)个, 每一个议题执行后主席会掉\(a_i\)的头发,不执行后议会会增加\(b_i\)的不开心值, 然后主席想让议会的不开心值最小,如果有多重方案就选自己头发掉的最少的: 而学生们想让主席的头发掉的最多,如果有多种方案让议会的不开心值最大. 问让议会同意哪\(p\)个会达到最好的效果. 思路1: 这是我的不对的思路. (虽然没提交 我们首先将所有的数按照

【Codeforces 429D】 Tricky Function

[题目链接] http://codeforces.com/problemset/problem/429/D [算法] 令Si = A1 + A2 + ... + Ai(A的前缀和) 则g(i,j) = Sj - Si f(i,j) = (i-j)^2 + (Si - Sj)^2 观察这个式子,我们发现可以用类似于平面最近点对的算法来求解该问题 [代码] #include<bits/stdc++.h> using namespace std; #define MAXN 100010 const

【Codeforces 105D】 Bag of mice

[题目链接] http://codeforces.com/contest/148/problem/D [算法] 概率DP f[w][b]表示还剩w只白老鼠,b只黑老鼠,公主胜利的概率,那么 : 1. 公主抓到白老鼠,概率为w/(w+b) 2. 公主抓到黑老鼠,那么龙也抓到黑老鼠,如果跑出来的老鼠是白颜色的,则概率为 : b / (w + b) * b / (w + b - 1) * w / (w + b - 2) * f[w-1][b-2] 否则,概率为 : b / (w + b) * (b -

【Codeforces 258A】 Game With Sticks

[题目链接] http://codeforces.com/contest/451/problem/A [算法] 若n和m中的最小值是奇数,则先手胜,否则后手胜 [代码] #include<bits/stdc++.h> using namespace std; int n,m; int main() { scanf("%d%d",&n,&m); if (min(n,m) % 2 == 0) printf("Malvika\n"); else

【Codeforces 258B】 Sort the Array

[题目链接] http://codeforces.com/contest/451/problem/B [算法] 模拟 在序列中找到一段单调递增的子序列,将这段序列反转,然后判断序列是否变得单调递增,即可 [代码] #include<bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 10; int i,n,l,r; bool flag; int a[MAXN]; int main() { scanf("%d"

【 CodeForces - 392C】 Yet Another Number Sequence (二项式展开+矩阵加速)

Yet Another Number Sequence Description Everyone knows what the Fibonacci sequence is. This sequence can be defined by the recurrence relation: F1 = 1, F2 = 2, Fi = Fi - 1 + Fi - 2 (i > 2). We'll define a new number sequence Ai(k) by the formula: Ai(