只放题解喽
题解
- T1
- T2
- T3
- T4
T1
等价于维护差分数组,数据范围较小,map 套 vector 维护即可
更大的数据范围可以 hash 做
T2
神奇贪心
本题关键在于定序,考虑顺序确定后答案怎么求
设 f i f_i fi 表示 第 i i i 件衣服烘干完的时间,显然有转移: f i = m a x ( f i − 1 , ∑ k = 1 i a k ) + b i f_i=max(f_{i-1},\sum_{k=1}^{i}a_k)+b_i fi=max(fi−1,∑k=1iak)+bi ,认为 f 0 = 0 f_0=0 f0=0
对于顺序,考虑邻项交换,贴一篇大佬的 blog
对于邻项 x , y x,y x,y,设前面烘干时间为 f f f, ∑ a k \sum a_k ∑ak 为 S S S ,显然我们希望让考虑完 x , y x,y x,y 后的 f f f 尽可能小
- 先 x x x 后 y y y, f x = m a x ( f , S + a x ) + b x f_x=max(f,S+a_x)+b_x fx=max(f,S+ax)+bx , f y = m a x ( f + b x + b y , S + a x + b x + b y , S + a x + a y + b y ) f_y=max(f+b_x+b_y,S+a_x+b_x+b_y,S+a_x+a_y+b_y) fy=max(f+bx+by,S+ax+bx+by,S+ax+ay+by)
- 先 y y y 后 x x x,将 ( x , y ) (x,y) (x,y) 轮换后可得到 f x ′ = m a x ( f + b y + b x , S + a y + b y + b x , S + a y + a x + b x ) f'_x=max(f+b_y+b_x,S+a_y+b_y+b_x,S+a_y+a_x+b_x) fx′=max(f+by+bx,S+ay+by+bx,S+ay+ax+bx)
何时可以不交换排好顺序的 x , y x,y x,y 呢?
显然应该 f y ≤ f x ′ ( 1 ) f_y\leq f'_x(1) fy≤fx′(1) ,开始化式子,由于 f + b x + b y f+b_x+b_y f+bx+by 相同,我们可以将其约掉
即: m a x ( S + a x + b x + b y , S + a x + a y + b y ) ≤ m a x ( S + a y + b y + b x , S + a y + a x + b x ) ( 2 ) max(S+a_x+b_x+b_y,S+a_x+a_y+b_y)\leq max(S+a_y+b_y+b_x,S+a_y+a_x+b_x) (2) max(S+ax+bx+by,S+ax+ay+by)≤max(S+ay+by+bx,S+ay+ax+bx)(2)
注意,这里约掉前后的两个式子是不等价的, m a x ( a , c ) ≤ m a x ( b , c ) max(a,c)\leq max(b,c) max(a,c)≤max(b,c) 不等价于 a ≤ b a\leq b a≤b,但在本题这么做不会出错,简单讨论可以发现是对的 (其实告诉我们这里只需要 化成必须交换的充分条件 就行)
接下来继续化:
a x + b y + m a x ( b x , a y ) ≤ a y + b x + m a x ( b y , a x ) a_x+b_y+max(b_x,a_y)\leq a_y+b_x+max(b_y,a_x) ax+by+max(bx,ay)≤ay+bx+max(by,ax)
m i n ( a x , b y ) ≤ m i n ( a y , b x ) min(a_x,b_y)\leq min(a_y,b_x) min(ax,by)≤min(ay,bx)
那么我们 c m p cmp cmp 函数里只需要这么写 m i n ( a x , b y ) < m i n ( a y , b x ) min(a_x,b_y)<min(a_y,b_x) min(ax,by)<min(ay,bx) 就做完了 —— 吗?
并不正确!关于邻项交换还有很多限制
对于我们定义的一种比较规则 X < Y X<Y X<Y,需要满足 严格弱序:
-
传递性,即 X < Y , Y < Z X<Y,Y<Z X<Y,Y<Z,就有 X < Z X<Z X<Z
否则,对于最终排好的序列中的 X , Y , Z X,Y,Z X,Y,Z,虽然对于邻项满足 X < Y , Y < Z X<Y,Y<Z X<Y,Y<Z,但是我们可以先交换 X , Y X,Y X,Y 使答案损失一部分,但接下来可能出现 X > Z X>Z X>Z,然后交换二者使得答案比以前更优 -
不可比性,若 X ≮ Y , Y ≮ X , Y ≮ Z , Z ≮ Y X≮Y,Y≮X,Y≮Z,Z≮Y X≮Y,Y≮X,Y≮Z,Z≮Y,那么 X ≮ Z , Z ≮ X X≮Z,Z≮X X≮Z,Z≮X
可以简单理解为 X = Y , Y = Z X=Y,Y=Z X=Y,Y=Z,那么 X = Z X=Z X=Z
否则,同上,交换 X , Y X,Y X,Y 后可能出现新的逆序,使得答案更优
本题来说,我们定义的比较规则 X < Y → m i n ( a x , b y ) < m i n ( a y , b x ) X<Y \to min(a_x,b_y)<min(a_y,b_x) X<Y→min(ax,by)<min(ay,bx),满足 1,但不满足 2
对于这个的验证方法参考大佬的 b l o g blog blog,大力发挥 OIer 的优势
那么接下来我们需要定义一种能够同时满足上述两个条件的比较方式
当 m i n ( a x , b y ) = m i n ( a y , b x ) min(a_x,b_y)=min(a_y,b_x) min(ax,by)=min(ay,bx) 时,我们感性理解可以把 a , b a,b a,b 小的放前面(这个其实比较随意,因为第一步已经保证了答案最优性,接下来只需要满足严格弱序就行),于是得到了:
{ m i n ( a x , b y ) < m i n ( a y , b x ) ( i f m i n ( a x , b y ) ≠ m i n ( a y , b x ) ) a x < a y ( o t h e r w i s e ) } \begin{Bmatrix} min(a_x,b_y)<min(a_y,b_x)\ \ \ (if\ \ min(a_x,b_y)\ne min(a_y,b_x))\\a_x<a_y \ \ \ \ \ (otherwise) \end{Bmatrix} {min(ax,by)<min(ay,bx) (if min(ax,by)=min(ay,bx))ax<ay (otherwise)}
这个比较方式既满足 严格弱序,又能够使得邻项交换后不会使得答案变优,于是我们就得到了正确答案
总结几点:
推式子时找充分条件就行了,不一定得充要
贪心时只考虑相邻两项,只要是满足严格弱序,且邻项交换不会使答案变优,那么就是最优方案
贴一个 checker
int a[N] , b[N] , w[N] ;
bool cmp ( int x , int y )
{if( min(a[x],b[y])!=min(a[y],b[x]) ) return min(a[x],b[y]) < min(a[y],b[x]) ;return a[x]<a[y] ;
}
bool check()
{int Max = 20 ;for( a[0] = 1 ; a[0] <= Max ; a[0] ++ ) {for( b[0] = 1 ; b[0] <= Max ; b[0] ++ ) {if( cmp(0,0) ) {printf("No self , %d %d\n" , a[0] , b[0] ) ;return 0 ;} // 验证自反性 for( a[1] = 1 ; a[1] <= Max ; a[1] ++ ) {for( b[1] = 1 ; b[1] <= Max ; b[1] ++ ) {if( cmp(0,1) && max(1/b[0],a[0]/b[1])>max(1/b[1],a[1]/b[0]) ) {printf("Not the best\n") ;return 0 ;} // 验证最优性 for( a[2] = 1 ; a[2] <= Max ; a[2] ++ ) {for( b[2] = 1 ; b[2] <= Max ; b[2] ++ ) {if( cmp(0,1) && cmp(1,2) && !cmp(0,2) ) {printf("No Trans\n") ;return 0 ;} // 验证传递性 if( !cmp(0,1)&&!cmp(1,0)&&!cmp(1,2)&&!cmp(2,1)&&(cmp(0,2)||cmp(2,0)) ) {printf("No comparability , (%d,%d) (%d,%d) (%d,%d)\n" , a[0] , b[0] , a[1] , b[1] , a[2] , b[2] ) ;return 0 ;} // 验证不可比性 }}} } }}return 1 ;
}
T3
大力发扬人类智慧!
先说暴力怎么做,枚举三角形,枚举点,判 点是否在三角形里,通过 面积法 来实现
已知两种 O ( n 4 w ) O(\frac{n^4}{w}) O(wn4) 做法:
-
求出每条直线上方 / 下方点集,可以 n 3 n^3 n3 预处理,接下来枚举三角形, b i t s e t bitset bitset 上上下下 与一下即可
-
注意到任选四个点都可以得到一组关于三角形的方程,也就是说需要花费 O ( n ) O(n) O(n) 计算权值的三角形不多,那么对于当前 i , j , k i,j,k i,j,k 三角形查找是否有 ( i , j , p ) , ( j , k , p ) , ( i , k , p ) (i,j,p),(j,k,p),(i,k,p) (i,j,p),(j,k,p),(i,k,p) 权值均已求过的 p p p,若没有则 O ( n ) O(n) O(n) 算,否则直接加加减减拼凑出来(及其恶心分讨)
下面说正解:
确定一个三角形就不得不枚举三个点了,尝试能不能枚举更少的点,来确定一些更基本的图形
对于每个点往 x x x 轴上打投影,发现只求梯形就行了!
S Δ A B C = S A D F C − S A B E D − S B E F C S_{\Delta ABC}=S_{ADFC}-S_{ABED}-S_{BEFC} SΔABC=SADFC−SABED−SBEFC
这样枚举两个点确定梯形,然后在 O ( n ) O(n) O(n) 求权值即可
有些小细节
T4
好题,难题
本题的第一个难点在于读懂题
注意到 “可以证明答案一定是一个整数” ,我们开始手玩
发现每次绳子扫过的面积 必定是以上一个关键点为左下角,下一个关键点为右上角的矩形
考虑两个限制:
选点的限制实际上是个二维偏序上的 L I S LIS LIS,排序+树状数组可以做掉
对于面积,写一下转移式:
d p i = m i n ( d p j + ( x i − x j ) ( y i − y j ) ) , l i s i = l i s j + 1 且 x j ≤ x i , y j ≤ y i dp_i=min(dp_j+(x_i-x_j)(y_i-y_j)),lis_i=lis_j+1\ 且\ x_j\leq x_i,y_j\leq y_i dpi=min(dpj+(xi−xj)(yi−yj)),lisi=lisj+1 且 xj≤xi,yj≤yi
拆开式子感觉和斜优很像,但很遗憾这个关于 j j j 的 双变量 式子不会做,而且也很难直接维护 Max 什么的
考虑限制分开做,先做 L I S LIS LIS ;然后按 L I S LIS LIS 分层,只考虑相邻层间的转移
容易发现:每层中 随 x x x 增大, y y y 单调不升
那么对于第 i i i 层的点 j j j, i − 1 i-1 i−1 层中合法的决策 p p p 是一段连续的区间…
然后…优化不动了,但我们发现这样写完交上去获得了 95pts 的好成绩!(逆天)
接下来,找找性质吧,然后就神奇的发现了这个有决策单调性
考虑四边形不等式,设 W ( i , j ) W(i,j) W(i,j) 表示下一层的 i i i 与本层的 j j j 构成的矩形面积
对于 a < b , i < j a< b,i< j a<b,i<j,有 W ( i , a ) + W ( j , b ) ≥ W ( i , b ) + W ( j , a ) W(i,a)+W(j,b)\geq W(i,b)+W(j,a) W(i,a)+W(j,b)≥W(i,b)+W(j,a) ( 1 ) (1) (1)
假设对于 i i i 来说, a a a 比 b b b 优,即 f a + W ( i , a ) ≤ f b + W ( i , b ) f_a+W(i,a)\leq f_b+W(i,b) fa+W(i,a)≤fb+W(i,b) ( 2 ) (2) (2)
( 1 ) + ( 2 ) (1)+(2) (1)+(2),可得 f a + W ( j , a ) ≤ f b + W ( j , b ) f_a+W(j,a)\leq f_b+W(j,b) fa+W(j,a)≤fb+W(j,b),即 对于 j j j 来说, a a a 比 b b b 优
所以具有决策单调性
但是,本题与之前朴素的分治优化/二分队列优化的题目不太一样,因为决策有上下界限制!!
必须要满足 x j ≤ x i , y j ≤ y i x_j\leq x_i,y_j\leq y_i xj≤xi,yj≤yi,那么随着 i i i 的枚举必然会删除、插入一些决策,与之前的只往后插入新决策不同
那么一个新的技巧:首先将 上一层 的所有决策建线段树,把 i i i 这个“询问”的合法决策范围拆成 l o g log log 个区间,放到上一层的线段树中,全部放完后,遍历线段树所有节点,对每个节点进行一次分治
这么做的好处是显然的,在一个在线段树节点中不用担心决策上下界变化,可以直接分治优化
分析复杂度:
考虑相邻两层,所有的询问一共会被放进 c n t × l o g cnt\times log cnt×log 个区间( c n t cnt cnt 表示每层的节点数),枚举询问的复杂度没问题
遍历线段树节点时,每个节点进行分治的复杂度是 l e n × l o g len\times log len×log,那么对于线段树一层的总复杂度是 c n t × l o g cnt\times log cnt×log,共 l o g log log 层,所以总复杂度 c n t × l o g 2 cnt\times log^2 cnt×log2,最后所有层的总复杂度就是 n l o g 2 nlog^2 nlog2
#include<bits/stdc++.h>
using namespace std ;typedef long long LL ;
const int N = 2e5+100 , M = 1e6+10 ; int n , m ;
struct nn
{int x , y ;
}a[N] ;
bool cmp( nn a , nn b )
{return a.x < b.x || (a.x==b.x&&a.y<b.y) ;
}
int f[N] ;
struct BIT
{int t[M] ;inline int lowbit( int x ) { return x&-x ; }void add( int p , int x ) {for( ; p <= m ; p += lowbit(p) ) t[p] = max( t[p] , x ) ;}int ask( int p ) {int res = 0 ;for( ; p ; p -= lowbit(p) ) res = max( res , t[p] ) ;return res ;}
} T ;
vector<int> ve[N] ;
// 达到 n^2 复杂度后再往下很难优化
// 观察 dp 式子十分朴素,考虑常用优化
// 发现是一个带上下界的决策单调性
// 决策上下界都会变化时:线段树 把询问分成log个区间,每个区间内可以正常进行分治
LL dp[N] ;
struct Segtree
{int l , r ;vector<int> q ;
}t[4*N] ;
void build( int p , int l , int r )
{t[p].l = l , t[p].r = r ;vector<int> tmp ;swap( tmp , t[p].q ) ;if( l == r ) {return ;}int mid = ( l + r ) >> 1 ;build( p<<1 , l , mid ) ; build( p<<1|1 , mid+1 , r ) ;
}
void push( int p , int l , int r , int x )
{if( l <= t[p].l && t[p].r <= r ) {t[p].q.push_back( x ) ;return ;}int mid = ( t[p].l + t[p].r ) >> 1 ;if( l <= mid ) push( p<<1 , l , r , x ) ;if( r > mid ) push( p<<1|1 , l , r , x ) ;
}
vector<int> q , J ;
inline LL Val( int x , int y ) // y->x
{return dp[y]+1LL*(a[x].x-a[y].x)*(a[x].y-a[y].y) ;
}
void solve( int l , int r , int ql , int qr )
{if( ql > qr ) return ;int mid = (ql+qr) >> 1 , j ;LL V = 1e18 ;for(int i = l ; i <= r ; i ++ ) {if( V > Val(q[mid],J[i]) ) {V = Val(q[mid],J[i]) ;j = i ;}}dp[q[mid]] = min( dp[q[mid]] , V ) ;solve( j , r , ql , mid-1 ) ;solve( l , j , mid+1 , qr ) ;
}
void work( int p )
{if( t[p].q.size() > 0 ) {swap( q , t[p].q ) ;solve( t[p].l , t[p].r , 0 , q.size()-1 ) ;}if( t[p].l == t[p].r ) {return ;}work( p<<1 ) ; work( p<<1|1 ) ;
}int main()
{scanf("%d%d" , &n , &m ) ;for(int i = 1 ; i <= n ; i ++ ) {scanf("%d%d" , &a[i].x , &a[i].y ) ;}a[n+1].x = m , a[n+1].y = m ;sort( a+1 , a+n+1 , cmp ) ;int Max = 0 ;ve[0].push_back( 0 ) ;for(int i = 1 ; i <= n ; i ++ ) {f[i] = T.ask( a[i].y ) + 1 ;T.add( a[i].y , f[i] ) ;ve[f[i]].push_back( i ) ;Max = max( Max , f[i] ) ;}Max ++ ;ve[Max].push_back( n+1 ) ;memset( dp , 0x3f , sizeof dp ) ;dp[0] = 0 ;for(int i = 1 ; i <= Max ; i ++ ) {build( 1 , 0 , ve[i-1].size()-1 ) ;int p = 0 , q = 0 ;for(int j : ve[i] ) {while( p < ve[i-1].size() && a[ve[i-1][p]].y > a[j].y ) p ++ ;while( q+1 < ve[i-1].size() && a[ve[i-1][q+1]].x <= a[j].x ) q ++ ;if( p <= q ) {push( 1 , p , q , j ) ;}}swap( J , ve[i-1] ) ;work( 1 ) ;swap( J , ve[i-1] ) ;}printf("%lld\n" , dp[n+1] ) ;return 0 ;
}