B. Bear and Strings
有一串字符串,从中取子串(必须包括bear),问有多少个子串。
bearbtear
6
对于第一个bear,前面的字符和后面的字符各取不同的个数可以组合为不同子串,
而对于第二个bear,前面的字符只能到第一个bear的e为止,否者就被第一个bear的子串集所包括。而后面的字符个数还是不限个数。
int main ()
{
int sum = 0, len, i, j;
char str[5005];
cin>>str;
len = strlen(str);
i = 0;
char s[] = "bear";
i = strstr(str, s) - str;
if (i < 0) cout<<"0"<<endl;
else
{
sum += (i + 1) * (len - i - 3);
while(1)
{
j = strstr(str + i + 1, s) - str;
if (j < 0) break;
sum += (j - i) * (len - j - 3);
i = j;
}
cout<<sum<<endl;
}
return 0;
}
输入N个数,数字范围为 [2, 10^7], 其中F(x) 为 N个数字中x的倍数的个数。输入M对整数对(L,R)。接下来对于每对(L,R)做如下操作:
从[L,R]当中依次取出质数x,求F(x) 的和
6 5 5 7 10 14 15 3 2 11 3 12 4 4
9 7 0
- The first query l = 2, r = 11 comes. You need to count f(2) + f(3) + f(5) + f(7) + f(11) = 2 + 1 + 4 + 2 + 0 = 9.
- The second query comes l = 3, r = 12. You need to count f(3) + f(5) + f(7) + f(11) = 1 + 4 + 2 + 0 = 7.
- The third query comes l = 4, r = 4. As this interval has no prime numbers, then the sum equals 0.
这道题目我在比赛中把素数筛选出来, 然后将N个数字分别求质因数,算出F(x)。虽然之后求m次为O(1)。但之前的预处理复杂度高,TLE了。
有一个很巧妙的方法,就是一边筛选素数,一边处理F(x)的值。
using namespace std;
int p[maxn+10], a[maxn+10], s[maxn+10];
int main ()
{
int n;
scanf("%d", &n);
for (int i = 0, x; i < n; i++)
{
scanf("%d", &x);
a[x]++;
}
for (int i = 2; i <= maxn; i++)
{
if (!p[i]) for (int j = i; j <= maxn; j += i)
{
p[j] = 1;
s[i] += a[j];
}
s[i] += s[i - 1];
}
int m, l ,r;
scanf("%d", &m);
while(m--)
{
scanf("%d%d", &l, &r);
l = min(l, maxn);
r = min(r, maxn);
printf("%d\n", s[r] - s[l - 1]);
}
return 0;
}
D. Bear and Floodlight
一只想从小熊从X轴的L点走到R点,但所走的路必须被灯照亮。有N盏灯(最大为20),每盏灯在(X,Y)处,灯的张角为α。问小熊从L点出发最多能走多远(如果能到R,便停下,所以最大为R-L)
2 3 5 3 1 45 5 1 45
2.000000000

假设此时已经点亮了[L,D], 接下来任意选一盏还未点亮的灯,可以照亮多少路程? 在计算几何中有通过旋转向量变为另一个向量,有:
灯(x,y),从灯指向(D,0)的向量 (dx,dy)= (d - x, -y)
旋转之后(dx, dy) = (dx·cos(angle) - dy·sin(angle), dx·sin(angle) + dy·cos(angle))
需要注意,旋转之后可能向量指向第一象限,此时可以照亮到无穷远处。
接下来就是如何确定最远距离了,采用DP。
将20盏灯用二进制的不同位来表示,0代表没开,1代表开了。比如dp[6] = 110, 即第二和第三盏灯打开了,而其他灯没有开。接下来采用如下的状态转移方程:
dp[ i or 2j ] = max(dp[ i or 2j], dp[ i ] + cal() )
其中j为没有打开的灯,而cal()就是上述的计算方法。
从0开始遍历到2^n - 1,不断更新最值,最后得到的dp[2^n - 1]就是最大值。(注意一开始要将所有的dp[]初始化为L)
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<ctype.h>
#include<algorithm>
#include<string>
#define PI acos(-1.0)
#define maxn 1000
#define INF 1<<25
#define N 21
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;
using namespace std;
double dp[1 << N], sinn[N], coss[N], x[N], y[N], l, r, a;
double cal(double d, int i)
{
double dx = d - x[i], dy = -y[i];
double ddx, ddy;
ddx = dx * coss[i] - dy * sinn[i];
ddy = dx * sinn[i] + dy * coss[i];
if (ddy >= 0) return r;
return x[i] + dy / ddy * ddx;
}
int main ()
{
int n;
cin>>n>>l>>r;
for (int i = 0; i < n; i++)
{
scanf("%lf%lf%lf", x + i, y + i, &a);
a = a / 180 * M_PI;
sinn[i] = sin(a), coss[i] = cos(a);
}
for (int i = 0; i < 1 << n; i++) dp[i] = l;
for (int i = 0; i < 1 << n; i++)
{
for (int j = 0; j < n; j++) if (!(i & 1 << j))
dp[i | 1 << j] = max(dp[i | 1 << j], cal(dp[i], j));
}
printf("%.9lf\n", min(dp[(1 << n) - 1], r) - l);
return 0;
}