海亮 7.7 模拟赛
海亮7.7模拟赛
主打一个寄
\(T1\)的\(30pts\)显然 然后开始罚坐 乐!
\(1h\)后发现\(T3\)可以玄学搜一波 加上固输\(35pts\) 虽然不是正经分也还不错了
打完\(T4\)暴力 最后\(T2\)怒打主席树并爆炸 我不好说
#A. 骰子游戏
\(30pts\)显然 那么我们的目标就是找六个数 使得这六个数任意两两异或起来都是\(k\)的倍数
那么显然有\(0\)和\(k\) 那么为了互相没有影响(\(k\)小于等于60) 那么我们输出左移六位和十二位和它们互相异或的两个数即可
#include <bits/stdc++.h>
using namespace std;
const int N = 50;
inline int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , k;
signed main ()
{
n = read() , k =read();
cout << "Yes" << endl;
for ( int i = 1 ; i <= n ; i ++ )
cout << 0 << ' ' << k << ' ' << ( k << 6 ) << ' ' << ( ( k << 6 ) | k ) << ' ' << ( k << 12 ) << ' ' << ( ( k << 12 ) | k ) << endl;
return 0;
}
#B. 裁剪彩带
\(dp[i]\)表示到\(i\)点的最大价值 那么\(dp[i]=max \{ dp[j]+mex(j,i) \}\)
观察到值域只有\(20\) 那么我们可以用一个二进制数记录区间内有没有这一个颜色 用\(st\)表来维护即可
我们可以预处理所有mex为0-21的情况 贪心地取满足mex为0-21的情况的最靠后的位置 来进行转移 贡献就是\(j\) 这可以用二分实现
最后对于方案的输出 我们记录\(pre[i]\)表示这一个状态由哪一个状态转移来的即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid (l+r>>1)
const int N = 5e5 + 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;
}
vector<int> v;
int st[N][22] , n , a[N] , pre[N] , lg[N] , f[N];
int query ( int l , int r )
{
int k = lg[r-l+1];
return ( st[l][k] | st[r-(1<<k)+1][k] );
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
lg[0] = -1;
n = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , st[i][0] = ( 1 << a[i] );
for ( int i = 1 ; i <= n ; i ++ ) lg[i] = lg[i>>1] + 1;
for ( int j = 1 ; j <= 21 ; j ++ )
for ( int i = 1 ; i + ( 1 << j - 1 ) <= n ; i ++ )
st[i][j] = ( st[i][j-1] | st[i+(1<<j-1)][j-1] );
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 0 ; j <= 21 ; j ++ )
{
int temp = ( 1 << j ) - 1;
if ( ( query ( 1 , i ) & temp ) != temp ) continue;
int l = 1 , r = i;
while ( l <= r )
{
if ( ( query ( mid , i ) & temp ) == temp ) l = mid + 1;
else r = mid - 1;
}
//最后的l是不满足条件的第一个 r是满足条件的第一个 那么我们要从r的前一位开始转移 所以我们使用r
if ( f[i] < f[r-1] + j ) f[i] = f[r-1] + j , pre[i] = r - 1;
}
cout << f[n] << ' ';
while ( n )
{
v.push_back(pre[n]+1);
n = pre[n];
}
sort ( v.begin() , v.end() );a
cout << v.size() << endl;
for ( int i = 0 ; i < v.size() ; i ++ ) cout << v[i] << ' ';
return 0;
}
#C. 挑战NPC
最大流问题
将每一个点套路地拆成入点和出点 源点和出点连边 入点和汇点连边 然后跑最大流 我们就保证了每一个点的出边和入边均为\(1\)
如果最大流为\(0\) 那么是无解的情况
最后对于方案的输出 我们\(dfs\)一下 输出正向边为0的情况即可(实际上我不会/kel)
放上暴搜代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 25;
inline int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
vector < int > a[N] , temp , ans[N][N];
int cntt[N] , vis[N];
int n , m , st;
int head[N] , cnt;
struct node { int to , nxt; } e[N<<2];
void add ( int u , int v ) { e[++cnt] = { v , head[u] } , head[u] = cnt; }
void dfs1 ( int u )
{
if ( u == st && vis[st] ) { ans[st][++cntt[st]] = temp; return; }
if ( vis[u] ) return;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
vis[u] = 1;
temp.push_back(u);
dfs1 ( v );
temp.pop_back();
vis[u] = 0;
}
}
void dfs ( int u )
{
if ( u > n )
{
int flag = 1;
for ( int i = 1 ; i <= n ; i ++ ) if ( !vis[i] ) flag = 0;
if ( flag )
{
printf ( "YES\n" );
for ( int i = 1 ; i <= n ; i ++ )
{
if ( a[i].size() )
{
printf ( "%d " , a[i].size() );
for ( int j = 0 ; j < a[i].size() ; j ++ )
printf ( "%d " , a[i][j] );
putchar ( '\n' );
}
}
exit(0);
}
return;
}
if ( vis[u] ) dfs ( u + 1 );
st = u;
dfs1 ( u );//将所有可能回路处理出来
// cout << "dfs1end" << endl;
// for ( int i = 1 ; i <= cntt[st] ; i ++ , putchar('\n') )
// for ( int j = 0 ; j < ans[st][i].size() ; j ++ )
// printf ( "%d " , ans[st][i][j] );
// cout << endl;
for ( int i = 1 ; i <= cntt[st] ; i ++ )//一共有cntt[st]这些情况
{
for ( int j = 0 ; j < ans[st][i].size() ; j ++ ) vis[ans[st][i][j]] = 1;
a[st] = ans[st][i];
dfs ( u + 1 );
a[st].clear();
for ( int j = 0 ; j < ans[st][i].size() ; j ++ ) vis[ans[st][i][j]] = 0;
}
}
signed main ()
{
n = read() , m = read();
if ( n > 20 ) { printf ( "NO" ); return 0; }
for ( int i = 1 ; i <= m ; i ++ )
{
int u = read() , v = read();
add ( u , v );
}
dfs(1);
printf ( "NO" );
return 0;
}