Topcoder SRM 607 div1题解

Posted 一只蒟篛酱的日常~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Topcoder SRM 607 div1题解相关的知识,希望对你有一定的参考价值。

好久没来写了,继续继续。。。

Easy(250pts):

//前方请注意,样例中带有zyz,高能预警。。。

题目大意:给你一个字符串,中间有一些是未知字符,请你求出这个字符串的回文子串个数的期望值。数据满足字符最多2500个。

我们考虑每一个子串,它对答案的贡献度就是它是回文串的概率,那么我们扫一遍就可以了,

这样做时间复杂度O(n^3),显然过不去。

我们考虑一下对于一个子串,在判断其是回文串的时候,我们一定是从中间往两边扫的,那么其实中间这些子串我们已经统计过答案了,

也就是说,我们通过枚举中间点来统计答案就可以了,

时间复杂度O(n^2),代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 string s;
 4 double ans=0.0;
 5 int n;
 6 class PalindromicSubstringsDiv1
 7 {
 8     public:
 9     double expectedPalindromes(vector <string> S1, vector <string> S2)
10     {
11         for (int i=0;i<S1.size();i++) s+=S1[i];
12         for (int i=0;i<S2.size();i++) s+=S2[i];
13         n=s.length();
14         for (int i=0;i<n;i++)
15         {
16 //i means the middle point of the substring
17             double now=1.0;
18             int lx=i-1,rx=i+1;
19             while (lx>=0&&rx<n)
20             {
21                 if (s[lx]==?&&s[rx]==?) now=now*1.0/26.0;
22                 else if (s[lx]!=?&&s[rx]!=?&&s[lx]==s[rx]) now=now*1.0;
23                 else if (s[lx]!=?&&s[rx]!=?) now=0.0;
24                 else now=now*1.0/26.0;
25                 ans+=now;
26                 --lx;++rx;
27             }
28             now=1.0,lx=i,rx=i+1;
29             while (lx>=0&&rx<n)
30             {
31                 if (s[lx]==?&&s[rx]==?) now=now*1.0/26.0;
32                 else if (s[lx]!=?&&s[rx]!=?&&s[lx]==s[rx]) now=now*1.0;
33                 else if (s[lx]!=?&&s[rx]!=?) now=0.0;
34                 else now=now*1.0/26.0;
35                 ans+=now;
36                 --lx,++rx;
37             }
38         }
39         ans+=1.0*n;
40         return ans;
41     }
42 };

Medium(475pts):

题目大意:有一串数字(0~9)围成了一个环,每一次可以选取一段区间,然后同时+1或者-1,(0 -1变成了9,9 +1变成了0)求现在到目标的最少操作次数。数据满足最多2500个元素。

这题怎么就475分了啊。。。感觉这么难。。。

容易观察到,结果只在%10意义下有用,所以先处理出差%10的余数。

接下来需要一个观察:如果两个区间一个是+操作,一个是-操作,那么一定可以做到这两个区间不相交。

然后考虑dp,f[i][j][k]表示当前在第i位,区间已经改变了j,而k则代表是+操作还是-操作。

这样时间复杂度是O(n^3)的,可以通过div2那个题。

然后我就跑去看官方题解了,然后没看懂。。。

大概的感受就是,观察出其实一个区间的操作改变不会很多,就只有-10~10以内,然后判一下就好了。

时间复杂度O(n^2),带一个常数,代码如下:

 1 #include <bits/stdc++.h>
 2 #define Maxn 2507
 3 #define Maxm 5507
 4 #define inf 200000007
 5 using namespace std;
 6 string S,T;
 7 int n;
 8 int d[Maxn];
 9 int f[Maxn][Maxm][2];
10 bool vis[Maxn][Maxm][2];
11 class CombinationLockDiv1
12 {
13     int tryit(int pos, int x, int dir)
14     {
15 //pos means which number it is dealing with now
16 //x means the addition of the interval
17 //dir means the interval is to add or to minus
18         if (vis[pos][x][dir]) return f[pos][x][dir];
19         vis[pos][x][dir]=true;
20         if (pos==n)
21         {
22             f[pos][x][dir]=0;
23             return f[pos][x][dir];
24         }
25         f[pos][x][dir]=inf;
26         for (int newdir=0;newdir<=1;newdir++)
27         {
28             for (int y=max(x-9,0);y<=min(x+9,5500);y++)
29             {
30                 if (newdir==0&&((d[pos]-y%10+10)%10!=0)) continue;
31                 if (newdir==1&&((d[pos]+y)%10!=0)) continue;
32                 int z;
33                 if (newdir!=dir) z=y; else z=max(y-x,0);
34                 f[pos][x][dir]=min(f[pos][x][dir],z+tryit(pos+1,y,newdir));
35             }
36         }
37         return f[pos][x][dir];
38     };
39     public:
40     int minimumMoves(vector <string> P, vector <string> Q)
41     {
42         for (int i=0;i<P.size();i++) S+=P[i];
43         for (int i=0;i<Q.size();i++) T+=Q[i];
44         n=S.size();
45         for (int i=0;i<n;i++) d[i]=(S[i]-T[i]+10)%10;
46         memset(vis,false,sizeof(vis));
47         memset(f,0,sizeof(f));
48         return tryit(0,0,0);
49     }
50 };

Hard(1000pts):

题目大意:有n个轮子,半径间隔完全相等地一横排排在一条直线上,有个起点和终点完全对称(到最近的轮子距离也相同,也在x轴上),现在有根绳子,可以任意地从S开始环绕轮子,然后到T。这样的绳子显然有无数多条,给定k,求第k短的长度。数据满足n<=50,k<=10^18。

这题的第一步非常难,也非常关键。(然而cyand1317表示并不难。。。)

所有的绳子都可以划分成四种的拼凑,第一种是从起点到上半部分,第二种是轮子的上半部分到下一个轮子的上半部分,第三种是轮子的上半部分到下一个轮子的下半部分,第四种是包围一个轮子半圈(轮子的上半部分到轮子的下半部分)。

然后我们发现,第一种对于所有绳子都恰好有两段,于是我们只需要考虑后三段就可以了,表示成一个三元组(x,y,z)。

我们先dp预处理出每一个状态的方案数,我们需要四维x,y,z以及一个数字表示方向。

然而实际上不需要,第四种情况的奇偶性就可以判断方向,然后直接O(n^3)的dp就可以了,

最后我们需要二分答案,然后来判定。

需要注意的是,本题数据这么大,显然是存不下的,我们需要设定一个inf,当数大于inf的时候,直接不参与计算。

时间复杂度O(n^3),代码如下:

 1 #include <bits/stdc++.h>
 2 #define inf (1LL<<60)
 3 using namespace std;
 4 double A,B,R;
 5 int n;
 6 long long combination[100007][47];
 7 long long f[77][77][67][2];
 8 class PulleyTautLine
 9 {
10     long long calc(long long n, long long k)
11     {
12         k=min(k,n-k);
13         if (k==0) return 1;
14         if (k==1) return n;
15         if (k==2&&0.5*n*(n-1)<1.1*inf) return 1LL*n*(n-1)/2;
16         if (k==3&&1.0/6*n*(n-1)*(n-2)<1.1*inf) return 1LL*n*(n-1)*(n-2)/6;
17         if (k<40&&n<100000) return combination[n][k];
18         return inf;
19     }
20     long long tryit(double len)
21     {
22         long long res=0;
23         for (int i=0;i<70;i++)
24             for (int j=0;j<70;j++)
25             {
26 //number of moves A&B
27 //number of moves R
28                 if (f[i][j][n-1][0]>0)
29                 {
30                     for (int k=0;k<=i;k++)
31                     {
32 //number of moves A
33                         double L=k*A+(i-k)*B+j*R;
34                         if (L>len) continue;
35 //max number of circles
36                         long long cir=(long long)((len-L)/2.0/R);
37                         long long cnt1=calc(i,k),cnt2=calc(cir+i+1,i+1);
38                         if (cnt2>inf/cnt1/f[i][j][n-1][0]) return inf;
39                         res+=1LL*cnt1*cnt2*f[i][j][n-1][0];
40                         if (res>inf) return inf;
41                     }
42                 }
43             }
44         return res;
45     }
46     public:
47     double getLength(int d, int r, int N, long long k)
48     {
49         n=N;
50         A=d,B=sqrt((double)d*d-4.0*r*r)+2.0*r*asin(2.0*r/d),R=r*acos(-1.0);
51         double L=sqrt((double)d*d-(double)r*r)+r*asin((double)r/d);
52         if (n==1) return (k-1)/2*2.0*R+2.0*L;
53         memset(combination,0,sizeof(combination));
54         for (int i=0;i<=100000;i++)
55         {
56             combination[i][0]=1;
57             for (int j=1;j<=40;j++)
58                 combination[i][j]=min(inf,combination[i-1][j]+combination[i-1][j-1]);
59         }
60         memset(f,0,sizeof(f));
61         f[0][0][0][0]=2;
62         for (int i=0;i<70;i++)
63             for (int j=0;j<70;j++)
64                 for (int k=0;k<n;k++)
65                     for (int p=0;p<=1;p++)
66                     {
67 //there are three kinds of moves
68 //one is to change the direction
69 //the other two are move forward (become nearer or farer)
70                         if (p==0) f[i][j+1][k][1]=min(inf,f[i][j+1][k][1]+f[i][j][k][p]);
71                         if (j%2==0&&k<n-1) f[i+1][j][k+1][0]=min(inf,f[i+1][j][k+1][0]+f[i][j][k][p]);
72                         if (j%2==1&&k>0) f[i+1][j][k-1][0]=min(inf,f[i+1][j][k-1][0]+f[i][j][k][p]);
73                     }
74         double left=0.0,right=1000.0*A,mid;
75         for (int i=1;i<=50;i++) 
76         {
77             mid=(double)(left+right)/2;
78             if (tryit(mid)>=k) right=mid; else left=mid;
79         }
80         return mid+2.0*L;
81     }
82 };

 

以上是关于Topcoder SRM 607 div1题解的主要内容,如果未能解决你的问题,请参考以下文章

Topcoder SRM 605 div1 题解

Topcoder SRM 603 div1题解

Topcoder SRM 608 div1 题解

Topcoder SRM 604 div1题解

TopCoder SRM502 Div1 1000 动态规划

topcoder srm 335 div1