1. 程式人生 > 其它 >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;
}