海亮 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
*/