A题思路:最主要的点就是要得出这个结论: 设p[i]为以i为中心的回文半径-1,那么如果满足j-i<=min(p[i],p[j]),那么j就可以和i组成究极回文串。
所以由这个式子可以看出三点:
1,i < j
2,j-i < p[i]
3,j-i < p[j]
由第三个可以得出i>j+p[j],所以算可以和i组成究极回文串的时候得将所有满足j+p[j]的j算进去,再计算满足小于i+p[i]的j的个数。这里可以用bit
#include
#include
#include
#include
using namespace std;
const int maxn = 500005+10;
char str[maxn];
char tmp[maxn << 1];
int Len[maxn << 1];
int INIT()
{
int i, len = strlen(str);
tmp[0] = '@';
for (i = 1;i <= len;i ++)
{
tmp[i] = str[i - 1];
}
tmp[len + 1] = '#';
tmp[ len + 2] = '$';
tmp[len + 3] = '\0';
return len + 1;
}
void MANACHER(int len)
{
int mx = 0, ans = 0, po = 0;
for (int i = 1;i <= len;i++)
{
if (mx > i)
Len[i] = min(mx - i, Len[2 * po - i]);
else
Len[i] = 1;
while (tmp[i - Len[i]] == tmp[i + Len[i]])
Len[i]++;
if (Len[i] + i > mx)
{
mx = Len[i] + i;
po = i;
}
}
}
vector<int>V[maxn];
#define lowbit(x) x&-x
int sum[maxn];
void add(int x, int num)
{
while (xint query(int x)
{
int ans = 0;
while (x)
{
ans += sum[x];
x -= lowbit(x);
}
return ans;
}
int p[maxn];
int str_len;
void init()
{
memset(sum, 0, sizeof(sum));
for (int i = 0;i < str_len+5;i++)V[i].clear();
memset(Len, 0, sizeof(Len));
}
int main()
{
int T;
cin >> T;
while (T--)
{
scanf("%s", str);
str_len = strlen(str);
init();
int changelen=INIT();
MANACHER(changelen);
for (int i = 1;i < changelen;i ++)
{
int pp = Len[i] - 1;
V[i - pp].push_back(i);
p[i] = pp;
}
long long ans = 0;
for (int i = 1;i <= str_len;i++)
{
for (int j = 0;j < V[i].size();j++)
{
add(min(str_len,V[i][j]), 1);
}
ans += query(min(i + p[i],str_len)) - query(i);
}
printf("%lld\n", ans);
}
return 0;
}
B题题意:求一个序列的所有子序列中第k大的数组成的序列中第M大的数。
思路:对于一个数来说,它是否是组成的序列中的第M大可以这样判断:在原序列的所有子序列中的第k大的数中,大于该数字的个数超过了M,那么答案肯定是比该数大的,否则比答案小。所以这里采用二分,而求个数可以采用尺取法。
代码简短可以自行推理。
#include
using namespace std;
const int maxn = 100005;
int a[maxn];
typedef long long ll;
int N, K;
ll M;
bool check(int num)
{
int j = 1;
int tot = 0;
ll ans = 0;
for (int i = 1;i <= N;i++)
{
if (a[i] >= num)
tot++;
if (tot == K)
{
ans += N - i + 1;
while (a[j] < num)
{
ans += N - i + 1;
j++;
}
tot--;
j++;
}
}
if (ans >= M)return 1;
return 0;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d %d %lld", &N, &K, &M);
for (int i = 1;i <= N;i++)
scanf("%d", &a[i]);
int l = 1, r = 1e9 + 5;
int mid;
while (l<=r)
{
mid = l + r >> 1;
if (check(mid))
l = mid+1;
else
r = mid - 1;
}
printf("%d\n", r);
}
return 0;
}
M题:思路:随机化+计算几何,不是很难,主要是第一次写随机化。
#include
#include
#include
#include
#include
using namespace std;
#define eps 1e-8
#define fabs(x) ((x)>0?(x):-(x))
#define zero(x) (fabs(x)
#define _sign(x) ((x)>eps?1:((x)<-eps?2:0))
#define sqr(x) ((x)*(x))
#define MAXN 100005
const double pi = acos(-1);
struct point
{
int idx;
double ang;
double x, y;
point() { x = 0;y = 0; }
point(double sx, double sy)
{
x = sx;y = sy;
}
void read()
{
scanf("%lf %lf", &x, &y);
}
};
struct line
{
double ang;
point a, b;line() {};
line(const point &p1, const point &p2)
{
a = p1;b = p2;
}
};
typedef const point CP;
typedef const line CL;
point P[MAXN];
int N;
struct make
{
double dis(CP &p1, CP &p2)
{
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
}
point intersection(CL &u, CL &v)
{
point ret = u.a;
double t = ((u.a.x - v.a.x)*(v.a.y - v.b.y) - (u.a.y - v.a.y)*(v.a.x - v.b.x)) / ((u.a.x - u.b.x)*(v.a.y - v.b.y) - (u.a.y - u.b.y)*(v.a.x - v.b.x));
ret.x += (u.b.x - u.a.x)*t;
ret.y += (u.b.y - u.a.y)*t;
return ret;
}
point circumcenter(CP &a, CP &b, CP &c)
{
line u, v;
u.a.x = (a.x + b.x) / 2;
u.a.y = (a.y + b.y) / 2;
u.b.x = u.a.x - a.y + b.y;
u.b.y = u.a.y + a.x - b.x;
v.a.x = (a.x + c.x) / 2;
v.a.y = (a.y + c.y) / 2;
v.b.x = v.a.x - a.y + c.y;
v.b.y = v.a.y + a.x - c.x;
return intersection(u, v);
}
}Making;
bool check(point u, double r)
{
int tot = 0;
for (int i = 1;i <= N;i++)
{
if (zero(Making.dis(P[i], u) - r))
tot++;
}
if (tot >= (N - 1) / 2 + 1)
return 1;
return 0;
}
void solve()
{
if (N == 1)
printf("%lf %lf 0\n", P[1].x, P[1].y);
else if (N <= 4)
{
point temp = { (P[1].x + P[2].x) / 2, (P[1].y + P[2].y) / 2 };
double r = Making.dis(P[1], P[2]) / 2;
printf("%lf %lf %lf\n", temp.x, temp.y, r);
}
else
{
while (1)
{
int a = ((rand() << 15 | rand()) % N + N) % N + 1;
int b = ((rand() << 15 | rand()) % N + N) % N + 1;
int c = ((rand() << 15 | rand()) % N + N) % N + 1;
while (a == b)
b = ((rand() << 15 | rand()) % N + N) % N + 1;
while (c == a || c == b)
c = ((rand() << 15 | rand()) % N + N) % N + 1;
point d = Making.circumcenter(P[a], P[b], P[c]);
double r = Making.dis(d, P[a]);
if (check(d, r))
{
//cout << a << endl;
printf("%lf %lf %lf\n", d.x, d.y, r);
break;
}
}
}
}
int main()
{
srand(time(NULL));
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d", &N);
for (int i = 1;i <= N;i++)
P[i].read();
solve();
}
return 0;
}