首页 > 代码库 > Topcoder SRM 607 div1题解

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题解