2021-06-25 集训题解
T1 硬币游戏
Description
Solution
不难看出,可以将 \(b_i\) 加上 \(a_i\),那么可以视作两种操作,一个是加上权重为 \(1\) 的 \(a_i\),另一个是加上权重为 \(2\) 的 \(b_i\),然后你发现限制没了,只需要权重 \(=k\),直接排序之后乱搞就好了。
代码就不放了。
T2 序列计数
Description
Solution
可以设 \(f_{S,x}\) 表示字符串 \(S\) 中以 \(x\) 结尾的本质不同子序列个数。
然后你发现转移可以写成矩阵形式,而且可逆,所以就直接预处理出前缀积和前缀逆即可。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define mod 1000000007
#define MAXN 500005
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
#define up 10
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
void Add (int &a,int b){a = a + b >= mod ? a + b - mod : a + b;}
char s[MAXN];
int n,q,sum[11],tag[11],A[11][11],B[11][11],f1[MAXN][11],f2[MAXN][11];
signed main(){
freopen ("sequence.in","r",stdin);
freopen ("sequence.out","w",stdout);
scanf ("%s",s + 1),n = strlen (s + 1);
for (Int i = 0;i <= up;++ i) A[i][i] = B[i][i] = sum[i] = 1;f2[0][up] = 1;
for (Int i = 1;i <= n;++ i){
int c = s[i] - 'a';
for (Int j = 0,t;j <= up;++ j){
t = A[j][c],A[j][c] = sum[j],f1[i][j] = sum[j] = dec (mul (sum[j],2),t);
t = B[c][j],B[c][j] = dec (mul (B[c][j],2),tag[j]),f2[i][j] = dec (B[up][j],tag[j] = t);
}
}
read (q);
while (q --> 0){
int l,r,ans = 0;read (l,r);
for (Int i = 0;i <= up;++ i) Add (ans,mul (f1[r][i],f2[l - 1][i]));
write (dec (ans,1)),putchar ('\n');
}
return 0;
}
T3 最大面积
Description
Solution
可以想到的是,对于一个区间 \([L,R]\) 的答案,实际上可以视作向量之和与 \(P\) 的叉乘。所以就诞生了一个 \(\Theta(n^2\log n)\) 的做法,就是说可以把每个区间对应的点都建出来,可以看出答案一定在凸壳上直接二分即可。
假设 \(T(L,R)\) 表示区间 \([L,R]\) 表示的点,那么你可以看出 \(T(L,R)=T(L,x)+T(x+1,R)\),那么你就可以直接分治再用闵可夫斯基和求出凸壳。
需要注意的是 \(x\) 正负不同的时候最值不同(一个求最小,一个求最大),所以需要存两个,复杂度是 \(\Theta(n\log^2n+m\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 200005
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
int n,m,mn,mx,sv1,sv2;
struct Vector{
int x,y;
int operator * (const Vector &p)const{return x * p.y - y * p.x;}
Vector operator + (const Vector &p)const{return Vector {x + p.x,y + p.y};}
Vector operator - (const Vector &p)const{return Vector {x - p.x,y - p.y};}
bool operator < (const Vector &p)const{return x != p.x ? x < p.x : y < p.y;}
}A[MAXN];
int tp1,tp2,siz[2];
Vector p[MAXN],st1[MAXN],st2[MAXN],pnt[2][MAXN];
double Slope (Vector A){
return (double)A.y * 1.0 / A.x;
}
void Convex (Vector *sta,int &top,Vector *p,int len){
top = 0,sort (p + 1,p + len + 1);
for (Int i = 1;i <= len;++ i){
if (top && sta[top].x == p[i].x) -- top;
while (top > 1 && Slope(p[i] - sta[top - 1]) > Slope(sta[top] - sta[top - 1])) -- top;
sta[++ top] = p[i];
}
}
void Conv (Vector *Sta,int &top,int l,int r,int xs){
int len = 0;
for (Int i = l;i <= r;++ i) p[++ len] = Vector{A[i].x * xs,A[i].y * xs};
Convex (Sta,top,p,len);
}
void Solveit (int l,int r){
if (l == r) return ;
int mid = (l + r) >> 1;
Solveit (l,mid),Solveit (mid + 1,r);
for (Int k = 0;k < 2;++ k){
Conv (st1,tp1,mid + 1,r,k ? -1 : 1),Conv (st2,tp2,l,mid,k ? 1 : -1);
int vx = st1[1].x + st2[1].x,vy = st1[1].y + st2[1].y,t = 0;
for (Int i = 1;i < tp1;++ i) p[++ t] = st1[i + 1] - st1[i];
for (Int i = 1;i < tp2;++ i) p[++ t] = st2[i + 1] - st2[i];
sort (p + 1,p + t + 1,[](Vector x,Vector y){return Slope (x) > Slope(y);});
pnt[k][++ siz[k]] = Vector{vx,vy};
for (Int i = 1;i <= t;++ i) vx += p[i].x,vy += p[i].y,pnt[k][++ siz[k]] = Vector{vx,vy};
}
}
signed main(){
freopen ("area.in","r",stdin);
freopen ("area.out","w",stdout);
read (n,m);
for (Int i = 1;i <= n;++ i){
read (A[i].x,A[i].y),A[i] = A[i - 1] + A[i];
chkmin (sv1,A[i].x - mx),chkmax (mx,A[i].x),chkmax (sv2,A[i].x - mn),chkmin (mn,A[i].x);
}
Solveit (0,n),Convex (pnt[0],siz[0],pnt[0],siz[0]),Convex (pnt[1],siz[1],pnt[1],siz[1]);
while (m --> 0){
int x,y;read (x,y);
if (x == 0) write (max (-sv1 * y,-sv2 * y)),putchar ('\n');
else{
int k = x < 0,ans = 0,l = 1,r = siz[k];
if (x < 0) x *= -1,y *= -1;Vector P = Vector{x,y};
while (l < r){
int mid1 = (l + r) >> 1,mid2 = mid1 + 1;
int v1 = P * pnt[k][mid1],v2 = P * pnt[k][mid2];
if (v1 > v2) chkmax (ans,v1),r = mid1;
else chkmax (ans,v2),l = mid2;
}
write (ans),putchar ('\n');
}
}
return 0;
}