2021-06-25 集訓題解
阿新 • • 發佈: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;
}