海亮 7.17 模拟赛

海亮 7.17 模拟赛

\(135pts->95pts\) 只因\(\_\_int128\)

实际上\(T3\)暴力不应该没打出来的()

#A. 学数学(math)

一眼丁真 鉴定为:规律题

打表可以发现符合条件的"(本身,立方)"类点对都是答案

然后对于这些立方数 我们可以通过\(30=8*4-2\)这类的式子推出其他处理规律

具体见代码 需要\(int128\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int __int128
const int N = 1e7 + 5;
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

inline void print(int x)
{
	if (x < 0) cout.put('-'), x = -x;
	if(x > 9)
		print(x / 10);
	cout.put(x % 10 + '0');
	return;
}

int n , ans[N] , cnt , base[N];

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	for ( int i = 1 ; i <= 1000000 ; i ++ ) ans[++cnt] = i * i * i , base[cnt] = i;
	for ( int i = 1 ; i <= 1000000 ; i ++ )
	{
		int cheng = base[i] * base[i] , jian = base[i] , res = ans[i];
		while(1)
		{
			int tempjian = jian;
			jian = res;
			if ( res * cheng - tempjian <= 1e18 && res * cheng - tempjian > 0 ) ans[++cnt] = ( res = res * cheng - tempjian );
			else break;
		}
	}
	sort ( ans + 1 , ans + cnt + 1 );
	int T = read();
	while ( T -- ) n = read() , print ( upper_bound ( ans + 1 , ans + cnt + 1 , n ) - ans - 1 ) , cout.put('\n');
	return 0;
}

#B. 序列(sequence)

可以得出一个合法的\(z\)一定是这个序列的前缀最大值/最小值

那么我们记录\(upp[i]\)\(downn[i]\)为以\(a[i]\)为开头的上升子序列个数 和下降子序列个数

用两个树状数组统计个数并塞进树状数组即可

最终答案就是\(\sum_{i=1}^n upp[i]*downn[i]\)

因为对于每一个位置作为首位置都是合法的 而且每一个位置 后面的上升序列和下降序列是可以自由组合的

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 1e5 + 5;
const int mod = 998244353;
const int inf = 0x3f3f3f3f3f3f3f3f;
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , upp[N] , downn[N] , a[N];//upp[N]表示的是以i为头的上升子序列个数和 downn同理 

struct bit
{
	int t[N];
	inline void clear() { for ( int i = 1 ; i <= n ; i ++ ) t[i] = 0; }
	inline int lowbit ( int x ) { return x & (-x); }
	void upd ( int x , int val ) { for ( int i = x ; i <= n ; i += lowbit(i) ) t[i] += val; }
	int query ( int x ) { int res = 0; for ( int i = x ; i ; i -= lowbit(i) ) res += t[i]; return res; }
}up , down;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		up.clear() , down.clear();
		for ( int i = 1 ; i <= n ; i ++ ) upp[i] = downn[i] = 0;
		n = read();
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		for ( int i = n ; i ; i -- )
		{
			upp[i] = ( up.query(n) - up.query ( a[i] ) + 1 ) % mod;
			up.upd ( a[i] , upp[i] );
			downn[i] = ( down.query(a[i]) + 1 ) % mod;
			down.upd ( a[i] , downn[i] );
		}
		int res = 0;
		for ( int i = 1 ; i <= n ; i ++ ) res = ( res + upp[i] * downn[i] % mod ) % mod;
		cout << res << endl;	
	}
	return 0;
}

#C. 区间(interval)

暴力代码(\(15pts\)) 考场上暴力的方向不对 应该是顺序枚举每一个区间放在哪一个组中

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long 
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , k , mark[N] , ans , vis[N];

struct DQY { int l , r; } a[N];

vector<DQY> vec[N];

int judge ( int m )
{
	int res = 0;
	for ( int i = 1 ; i <= m ; i ++ ) 
	{
		int l = 0 , r = inf;
		for ( auto t : vec[i] ) l = max ( l , t.l ) , r = min ( r , t.r );//取交集!!!!! 
		if ( l >= r ) return 0;
		res += r - l;
	}
	return res;
}

void dfs ( int stp , int cnt )//进行到第stp区间(这个区间还没搞 前面区间一共分成了cnt段 
{
	if ( n - stp + 1 < k - cnt ) return;
	if ( stp == n + 1 && cnt == k ) return ans = max ( ans , judge(cnt) ) , void();
	for ( int i = 1 ; i <= cnt ; i ++ )
	{
		vec[i].push_back(a[stp]);
		dfs ( stp + 1 , cnt );
		vec[i].pop_back();
	}
	if ( cnt < k )
	{	
		vec[cnt+1].push_back(a[stp]); 
		dfs ( stp + 1 , cnt + 1 );
		vec[cnt+1].pop_back();
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , k = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].l = read() , a[i].r = read();
	dfs ( 1 , 0 );
	cout << ans << endl;
	return 0;
}

正解:

我们可以看到 如果将一个大区间(可以包含小区间的区间)放在小区间的组别里 那么对于答案没有贡献

所以我们可以将所有大区间单提出来 对答案的贡献就是区间长度 计算分成\(i\)段的价值

剩下的区间我们考虑将它们插入子区间中

现在考虑将小区间分成\(k-i\)段的贡献 我们先按照左端点排序

设置\(f[i][j]\)表示第\(i\)个区间 将它和它前面的区间分成\(j\)段的最大价值

显然有\(f[i][j]=max(f[k][j-1]+a[k+1].r-a[i].l)\) 而且\(a[k+1].r-a[i].l>0\)

意思就是我们枚举最后一段的起始点\(k+1\) 那么整个区间的贡献由前\(k\)个区间分成\(j-1\)组 和最后一组中的区间组成

因为我们按照左端点排序 那么如\(a[k+1].r\le a[i].l\) 那么\(a[k+1].r\)一定小于\(a[i+1].l\) 这个值显然是彻底没用了

所以可以用以\(f[k][j-1]+a[k+1].r\)为值的单调队列来优化\(dp\)

注意:\(q\)初始化的时候推进去\(j-1\)的状态 因为你要将前面分成\(j-1\)段 那么至少序列中需要有\(j-1\)个元素 否则不够分了

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long  
const int N = 5000 + 5;
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , k , ans , vis[N] , cnt , g[N] , f[N][N];//g[N]是大区间的贡献 f[N][N]是小区间的贡献 
struct node { int val , id; } q[10000000];

struct DQY { int l , r; } a[N] , b[N];
vector<int> big;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , k = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].l = read() , a[i].r = read();
	for ( int i = 1 ; i <= n ; i ++ ) 
		for ( int j = 1 ; j <= n ; j ++ )
			if ( i != j && a[i].l <= a[j].l && a[j].r <= a[i].r && !vis[i] ) 
				vis[i] = 1 , big.push_back(a[i].r - a[i].l);
	sort ( big.begin() , big.end() , [](const int &a , const int &b) { return a > b; } );
	for ( int i = 0 ; i < big.size() ; i ++ ) g[i+1] = g[i] + big[i];
	
	for ( int i = 1 ; i <= n ; i ++ ) if ( !vis[i] ) b[++cnt] = a[i];
	sort ( b + 1 , b + cnt + 1 , [](const DQY &a , const DQY &b) { return a.l < b.l; } );
	
	memset ( f , -0x3f , sizeof f );
	f[0][0] = 0;
	for ( int j = 1 ; j <= k ; j ++ )
	{
		int head = 1 , tail = 0;
		q[++tail] = { f[j-1][j-1] + b[j].r , j - 1 };//因为你要将前面分成j-1段 那么至少序列中需要有j-1个元素 否则不够分了 
		for ( int i = j ; i <= cnt ; i ++ )
		{
			while ( head <= tail && b[q[head].id+1].r <= b[i].l ) head ++;
			if ( head <= tail ) f[i][j] = max ( f[i][j] , q[head].val - b[i].l );
			while ( head <= tail && q[tail].val < f[i][j-1] + b[i+1].r ) tail --;
			q[++tail] = { f[i][j-1] + b[i+1].r , i };
		}
	}
	for ( int j = 0 ; j <= big.size() && k > j ; j ++ )//这块的j不能到k 因为必须给big剩余的区间留一个小区间塞进去 
		ans = max ( ans , g[j] + f[cnt][k-j] );	
	cout << ans << endl;
	return 0;
}

/*
4 2
1 3
1 5
4 6
2 7

*/
posted @ 2023-07-17 16:11  Echo_Long  阅读(4)  评论(0编辑  收藏  举报