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 };