看800遍题解没懂咋做
不懂概率1/2怎么处理
有n天,每天有个物品,权值。
从n天中选择m天,每天从该天的若干个权值中选择1个。
有m个权值,使这m个权值的最大值和最小值的差值最小。
每个物品有两个属性(权值,天数)。
选择m个物品,这些物品的天数两两不同。
把这些物品按权值从小到大排序,然后滑动窗口扫一遍,注意对窗口内的天数打标记,认为这些天数已经用过了。
然后取最小值就行了。
#include
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define cl(x) x.clear()
#define all(x) x.begin() , x.end()
#define rep(i , x , n) for(int i = x ; i <= n ; i ++)
#define per(i , n , x) for(int i = n ; i >= x ; i --)
#define mem0(x) memset(x , 0 , sizeof(x))
#define mem_1(x) memset(x , -1 , sizeof(x))
#define mem_inf(x) memset(x , 0x3f , sizeof(x))
#define debug(x) cerr << '*' << x << '\n'
#define ddebug(x , y) cerr << '*' << x << ' ' << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair pii ;
typedef pair pll ;
const int mod = 998244353 ;
const int maxn = 2e6 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count()) ;
int n , m ;
struct node
{
int x , id , p ;
bool operator < (const node &s) const
{
if(x != s.x) return x < s.x ;
else if(id != s.id) return x < s.id ;
else return p < s.p ;
}
} b[maxn] ;
int cnt = 0 ;
bool vis[maxn] ;
int main()
{
ios ;
cin >> n >> m ;
rep(i , 1 , n)
{
int k ;
cin >> k ;
rep(j , 1 , k)
{
int x ;
cin >> x ;
b[++ cnt] = (node){x , i , cnt} ;
}
}
sort(b + 1 , b + cnt + 1) ;
int ans = inf ;
int l = 1 , r = 0 ;
int num = 0 ;
while(l < cnt)
{
while(num < m && r < cnt)
{
r ++ ;
if(vis[b[r].id]) continue ;
else
{
vis[b[r].id] = 1 ;
num ++ ;
}
}
if(num == m) ans = min(ans , b[r].x - b[l].x) ;
else break ;
while(num == m && l <= r)
{
if(!vis[b[l].id]) l ++ ;
else
{
vis[b[l].id] = 0 ;
l ++ ;
num -- ;
}
}
if(l <= r)
{
if(!vis[b[l].id])
{
vis[b[l].id] = 1 ;
num ++ ;
}
}
}
cout << ans << '\n' ;
return 0 ;
}
民间题解真好,我也真菜。
这个其实是最可补的一题。
需要深刻理解分治乘。
给一个n*m的0,1矩阵,求满足下列条件的子矩阵个数。
(1)四个边界全是1
(2)不包括边界的部分的0的个数和1的个数的差值的绝对值不超过1
(3)长和宽都大于1
数据范围:
看数据范围考虑
枚举上下边界,然后从左往右扫过去。
上下边界必须都是连续的1,因为内部个数的0,1差值不超过1,所以从左往右扫过去维护一下前缀和就可以在知道右边界时确定左边界的个数了。
#include
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define cl(x) x.clear()
#define all(x) x.begin() , x.end()
#define rep(i , x , n) for(int i = x ; i <= n ; i ++)
#define per(i , n , x) for(int i = n ; i >= x ; i --)
#define mem0(x) memset(x , 0 , sizeof(x))
#define mem_1(x) memset(x , -1 , sizeof(x))
#define mem_inf(x) memset(x , 0x3f , sizeof(x))
#define debug(x) cerr << '*' << x << '\n'
#define ddebug(x , y) cerr << '*' << x << ' ' << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair pii ;
typedef pair pll ;
const int mod = 998244353 ;
const int maxn = 500 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count()) ;
int n , m ;
int a[maxn][maxn] ;
int sum[maxn][maxn] ;
int pre1[maxn * maxn * 3] ;
int pre2[maxn * maxn * 3] ;
int offset = 500 * 500 ;
ll ans = 0 ;
int cal(int x1 , int y1 , int x2 , int y2)
{
if(x1 > x2 || y1 > y2) return 0 ;
return sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 - 1] ;
}
void solve(int i , int j , int l , int r)
{
rep(k , l , r)
{
if(cal(i , k , j , k) == j - i + 1)
{
int s1 = cal(i + 1 , l , j - 1 , k - 1) ;
int s2 = cal(i + 1 , l , j - 1 , k) ;
int temp1 = s1 + offset ;
int temp2 = s2 + offset ;
ans += pre2[temp1 - 1] + pre2[temp1] + pre2[temp1 + 1] ;
pre1[temp1] ++ ;
pre2[temp2] ++ ;
}
}
}
void move(int i , int j , int l , int r)
{
rep(k , l , r)
{
if(cal(i , k , j , k) == j - i + 1)
{
int s1 = cal(i + 1 , l , j - 1 , k - 1) ;
int s2 = cal(i + 1 , l , j - 1 , k) ;
int temp1 = s1 + offset ;
int temp2 = s2 + offset ;
pre1[temp1] = 0 ;
pre2[temp2] = 0 ;
}
}
}
int main()
{
ios ;
cin >> n >> m ;
rep(i , 1 , n) rep(j , 1 , m)
{
cin >> a[i][j] ;
if(a[i][j] == 0) a[i][j] = -1 ;
}
rep(i , 1 , n) rep(j , 1 , m) sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j] ;
rep(i , 1 , n) rep(j , i + 1 , n)
{
rep(k , 1 , m)
{
if(a[i][k] != 1 || a[j][k] != 1) continue ;
int p = k ;
while(p + 1 <= m && a[i][p + 1] == 1 && a[j][p + 1] == 1) p ++ ;
solve(i , j , k , p) ;
move(i , j , k , p) ;
k = p ;
}
}
cout << ans << '\n' ;
return 0 ;
}