CF1592
CF1592
Gamer Hemose
一定是用前两大的来打 那么将 \(a\) 排序后将 \(h\) 模上 \((a[n-1]+a[n])\) 即可 最后剩下一点血量特判即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define ls p<<1
#define rs p<<1|1
#define mid (l+(r-l>>1))
#define pii pair<int,int>
#define lson ls,l,mid
#define rson rs,mid+1,r
#define fi first
#define se second
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int inf = 0x3f3f3f3f;
const int N = 1e3 + 5;
int read()
{
int f = 1 , x = 0;
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 , h , a[N];
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int T = read();
while ( T -- )
{
n = read() , h = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
sort ( a + 1 , a + n + 1 );
int ans = h / ( a[n-1] + a[n] ) * 2;
h %= ( a[n-1] + a[n] );
if ( h == 0 ) { cout << ans << endl; continue; }
if ( h <= a[n] ) ans += 1;
else ans += 2;
cout << ans << endl;
}
return 0;
}
Hemose Shopping
一开始想的是对于一个不降段 判断能不能给它们转开 但是 \(WA\) 了 打开题解()
如果 \(x*2\le n\) 那么显然 我们可以任意移动两个点的位置
对于 \(x*2>n\) 的情况 一定有一部分点是不能移动的 那么我们将原数组的备份排序 对于不能移动的点就跟原数组比较看是否相等即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define ls p<<1
#define rs p<<1|1
#define mid (l+(r-l>>1))
#define pii pair<int,int>
#define lson ls,l,mid
#define rson rs,mid+1,r
#define fi first
#define se second
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5;
int read()
{
int f = 1 , x = 0;
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 , x , a[N] , b[N];
int check ()
{
sort ( b + 1 , b + n + 1 );
for ( int i = 1 ; i <= n ; i ++ )
if ( i - x < 1 && i + x > n )
if ( a[i] != b[i] ) return 0;
return 1;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int T = read();
while ( T -- )
{
n = read() , x = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = b[i] = read();
if ( x * 2 <= n ) cout << "YES" << endl;
else cout << ( check() ? "YES" : "NO" ) << endl;
}
return 0;
}
Bakry and Partitioning
诈骗题
如果整个树异或起来和为 \(0\) 的话 我们任意断掉一条边都是可以的
那么如果异或起来不是 \(0\) 的而是 \(c\) 的话 那么一定是三个连通块异或起来答案为 \(c\) (每一个连通块答案都为 \(c\) ) 是最优(如果可以有更多奇数连通块 我们可以将任意两个合并并消去)
我们对于这种情况在树上进行 \(dfs\) 即可 遇到一个异或起来为 \(c\) 的联通块就剪掉 最后统计是否有大于等于三个的奇数连通块即可
注意根节点不会被统计到 要特殊统计
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5;
int read()
{
int f = 1 , x = 0;
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 a[N] , n , k , res , cnt , sum[N];
vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }
void dfs ( int u , int ff )
{
sum[u] = a[u];
for ( auto v : e[u] )
if ( v ^ ff )
{
dfs ( v , u );
if ( sum[v] == res ) ++ cnt;
else sum[u] ^= sum[v];
}
if ( u == 1 && sum[u] == res ) ++ cnt;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int T = read();
while ( T -- )
{
res = cnt = 0;
n = read() , k = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , res ^= a[i] , e[i].clear();
for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
if ( res == 0 ) cout << "YES" << endl;
else if ( k >= 3 )
{
dfs(1,0);
cout << ( cnt >= 3 && cnt & 1 ? "YES" : "NO" ) << endl;
}
else cout << "NO" << endl;
}
return 0;
}
Hemose in ICPC ?
首先 \(gcd\) 是只减不增的 那么我们相当于是询问这棵树上的最大边
看到 \(1e3\) 的点集大小和 \(12\) 次询问 不难想到需要用 \(logn\) 次询问
那么我们先用一次询问来问出最大边权是多少 然后二分出这个边的两个端点
为了保证二分的每一个点集都是一个连通块 我们考虑在欧拉序上二分 这样可以保证连通性
在交互的时候我们必须为点集去重...
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int inf = 0x3f3f3f3f;
const int N = 2e3 + 5;
int read()
{
int f = 1 , x = 0;
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 a[N] , n , k , dfn[N] , cnt;
vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }
void dfs ( int u , int ff )
{
dfn[++cnt] = u;
for ( auto v : e[u] )
if ( v ^ ff )
{
dfs ( v , u );
dfn[++cnt] = u;
}
}
int check ( int l , int x )
{
set<int> s;
for ( int i = l ; i <= x ; i ++ ) s.insert(dfn[i]);
cout << "? " << s.size() << ' ';
for ( auto v : s ) cout << v << ' ';
cout << endl << flush;
return read();
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read();
for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
dfs ( 1 , 0 );
int l = 1 , r = cnt;
int res = check(l,r);
while ( r - l > 1 )
{
if ( check ( l , mid ) == res ) r = mid;
else l = mid;
}
cout << "! " << min ( dfn[l] , dfn[r] ) << ' ' << max ( dfn[l] , dfn[r] ) << endl << flush;
return 0;
}
Bored Bakry
考虑什么时候我们与起来才严格大于异或:
存在一个二进制位 在这个子段中二进制表示全都是 \(1\) 且比它更高的所有二进制位全部打平(即异或起来和与起来都是 \(0\) )
对于某一个特定的二进制位 \(d\) 由于我们需要这一段二进制都为 \(1\) 那么我们不会越过有 \(0\) 的二进制数 那么我们可以考虑在两个该位二进制为 \(0\) 的数之间进行统计
考虑为高于 \(d\) 位的二进制数记录一个 \(map[x]=i\) 表示上一个出现该状态 \(x\) 的最近位置 \(+1\)
那么我们记录一个前缀二进制状态 \(sum\) 来更新 \(map\) 如果 \(vis\) 过该状态那么统计 否则更新状态即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 5;
int read()
{
int f = 1 , x = 0;
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 , a[N] , ans;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
map<int,int> pre;
for ( int d = 0 ; d <= 20 ; d ++ )
{
pre.clear();
pre[0] = 1;
int sum = 0;
for ( int i = 1 ; i <= n ; i ++ )
{
if ( ( a[i] & ( 1 << d ) ) == 0 )
{
sum = 0 , pre.clear() , pre[0] = i + 1;
continue;
}
sum ^= ( a[i] >> d );
if ( !pre.count(sum) ) pre[sum] = i + 1;
else ans = max ( ans , i - pre[sum] + 1 );
}
}
cout << ans << endl;
return 0;
}
Alice and Recoloring 1
较为简单的 \(version\)
考虑对于整张图进行转化 \(B\) 为 \(1\) \(W\) 为 \(0\) 令每一个点的权值代表该节点本身作为左上角的 \(2\times 2\) 矩阵的异或奇偶性
因为顺推或者逆推都是一样的 那么我们将目标转化为让整张图的权值均变为 \(0\)
这样我们可以得出 \(1\) 操作只会修改想要的 \((x,y)\) 节点
\(4\) 操作会修改 \((n,m)\) \((x-1,y-1)\) \((x-1,m)\) \((n,y-1)\) 这四个点的权值
不难看出 \(23\) 操作都可以被 \(1\) 操作不劣地替代 但是 \(4\) 操作可以用 \(3\) 的代价修改
但是我们可以得出 \(4\) 操作只能进行一次 否则只会用 \(6\) 的代价修改 \(6\) 个点(\((n,m)\) 被修改了两次 这样是不优的
所以我们转化并枚举整张图 如果能进行 \(4\) 操作 那么答案减一输出即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int inf = 0x3f3f3f3f;
const int N = 500 + 5;
int read()
{
int f = 1 , x = 0;
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 a[N][N] , mp[N][N] , n , m , ans;
char ch;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1 ; j <= m ; j ++ )
cin >> ch , mp[i][j] = ( ch == 'B' );
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1 ; j <= m ; j ++ )
a[i][j] = mp[i][j] ^ mp[i+1][j] ^ mp[i][j+1] ^ mp[i+1][j+1] , ans += ( a[i][j] == 1 );
for ( int i = 1 ; i < n ; i ++ )
for ( int j = 1 ; j < m ; j ++ )
if ( a[i][j] && a[i][m] && a[n][j] && a[n][m] ) { cout << ans - 1 << endl; return 0; }
cout << ans << endl;
return 0;
}
Alice and Recoloring 2
和 \(version\ 1\) 的转化一样 但是这下 \(4\) 操作可以只用 \(2\) 的代价
那么显然 \(4\) 操作可以用 \(2\) 的代价修改三个点 \((x-1,y-1)\) \((x-1,m)\) \((n,y-1)\)
那么有两个结论:
-
一定在上述三个点都为 \(1\) 的时候才修改
这是因为如果有一个点不是 \(1\) 那么我们产生了一次错误的转化 最后还需要用一次 \(1\) 操作转化回来 这样和 \(1\) 操作是一样的
-
如果我们修改了 \((x,y)\) 一定不会再修改 \((x,y_0)\) 同理我们也不会修改 \((x_0,y)\)
这是因为如果这样修改 我们相当于是用了 \(2+2\) 的代价修改了 \(4\) 个点的权值 这和做四次 \(1\) 操作是一样的
那么我们做二分图匹配即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int inf = 0x3f3f3f3f;
const int N = 1e3 + 5;
int read()
{
int f = 1 , x = 0;
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 a[N][N] , mp[N][N] , n , m , ans , res , vis[N] , link[N];
char ch;
vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }
int hungry ( int u )
{
for ( auto v : e[u] )
if ( !vis[v] )
{
vis[v] = 1;
if ( link[v] == -1 || hungry(link[v]) ) return link[v] = u , 1;
}
return 0;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
memset ( link , -1 , sizeof link );
n = read() , m = read();
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1 ; j <= m ; j ++ )
cin >> ch , mp[i][j] = ( ch == 'B' );
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1 ; j <= m ; j ++ )
a[i][j] = mp[i][j] ^ mp[i+1][j] ^ mp[i][j+1] ^ mp[i+1][j+1];
for ( int i = 1 ; i < n ; i ++ )
for ( int j = 1 ; j < m ; j ++ )
if ( a[i][j] && a[i][m] && a[n][j] )
add ( i , j + n ) , add ( j + n , i );
for ( int i = 1 ; i < n ; i ++ ) res += hungry(i) , memset ( vis , 0 , sizeof vis );
ans -= res;
a[n][m] ^= ( res & 1 );
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1 ; j <= m ; j ++ )
ans += a[i][j];
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号