CF1515
CF1515
Phoenix and Gold
非常好随机化 爱来自 \(random\_shuffle\)()
显然如果我们随机一下序列,有概率让整个数组满足这种情况的。
实测随机 \(6\) 次是最小随机化次数。
下面是正解:
一个很玄学的思路,我们从前往后扫一遍数组,遇到前缀和不合法的,就交换 \(a_i\) 和 \(a_n\),因为这两个数题目中保证了一定不相同,所以可以起到更改前缀和的作用。
如果到最后前缀和不能为 \(x\) 那么是合法的,反之亦然。
随机化 \(code\):
#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 lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e2 + 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];
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] = read();
int flgg = 0;
for ( int i = 1 ; i <= 6 ; i ++ )
{
random_shuffle ( a + 1 , a + n + 1 );
int sum = 0 , flag = 1;
for ( int j = 1 ; j <= n ; j ++ )
{
sum += a[j];
if ( sum > x ) break;
if ( sum == x ) { flag = 0; break; }
}
if ( flag ) { flgg = 1; break; }
}
if ( flgg )
{
cout << "YES" << endl;
for ( int i = 1 ; i <= n ; i ++ ) cout << a[i] << ' ';
cout << endl;
}
else cout << "NO" << endl;
}
return 0;
}
正解:
#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 lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e2 + 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];
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] = read();
int sum = 0;
for ( int i = 1 ; i <= n ; i ++ )
{
if ( sum + a[i] == x ) swap ( a[i] , a[n] );
sum += a[i];
}
if ( sum != x )
{
cout << "YES" << endl;
for ( int i = 1 ; i <= n ; i ++ ) cout << a[i] << ' ';
cout << endl;
}
else cout << "NO" << endl;
}
return 0;
}
Phoenix and Puzzle
基础的正方形为两个或者四个,而且可以证明要么是两个要么是四个,不能同时使用两种类型的基础正方形,否则无法使边长相等。
那么如果 \(n\) 除上 \(2\) 或者 \(4\) 是完全平方数可以拼成,否则无解。
#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 lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e2 + 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];
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int T = read();
while ( T -- )
{
n = read();
int flag = 0;
if ( ! ( n % 2 ) )
{
if ( n / 2 == (int)sqrt(n/2) * (int)sqrt(n/2) ) flag = 1;
}
if ( ! ( n % 4 ) )
{
if ( n / 4 == (int)sqrt(n/4) * (int)sqrt(n/4) ) flag = 1;
}
cout << ( flag ? "YES" : "NO" ) << endl;
}
return 0;
}
Phoenix and Towers
一开始用优先队列维护整个序列中的合并情况,发现很不好统计,打出来 WA 了几发。
正难则反,我们可以用优先队列维护整个序列中的 \(m\) 个塔,从前往后扫一遍序列,对于每一个数,我们贪心地选取它在当前高度最低的塔上即可。
因为保证了每一个数都 \(\le x\),那么序列中的每两个塔之间的差值都 \(\le x\)。
那么为最低的塔高 \(val\) 加上一个 \(\le x\) 的值 \(k\),此时该塔高变为 \(val+k\),设此时的最小塔高为 \(val'\),那么 \(val\le val'\) 且 \(k\le x\) 那么一定有 \(val+k-val'\le x\),所以我们保证了按照我们的贪心策略,一定可以让序列中的每两个塔之间的差值都 \(\le x\)。
所以这样证明了原题没有无解的情况。
#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 lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
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 , m , x , a[N] , vis[N] , cnt , ans[N];
vector<int> vec;
struct node { int id , val; friend bool operator < ( const node &a , const node &b ) { return a.val > b.val; } };
priority_queue < node > q;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int T = read();
while ( T -- )
{
while ( !q.empty() ) q.pop();
n = read() , m = read() , x = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
for ( int i = 1 ; i <= m ; i ++ ) q.push({i,0});
for ( int i = 1 ; i <= n ; i ++ )
{
node u = q.top(); q.pop();
ans[i] = u.id;
u.val += a[i];
q.push(u);
}
cout << "YES" << endl;
for ( int i = 1 ; i <= n ; i ++ ) cout << ans[i] << ' ';
cout << endl;
}
return 0;
}
Phoenix and Socks
\(*1500\) 调了一个小时,什么水平。
首先 我们先将所有能匹配上的袜子进行配对,这样是绝对不劣的。
其次我们严格让左面的袜子多,方便我们进行处理,统计 \(cntl\) 为左面的袜子个数,\(cntr\) 为右面的袜子个数。
那么我们首先期望要用每次 \(1\) 的代价消除 \(cntl\) 和 \(cntr\) 之间的数量差距。
只要左面点有袜子的数量 \(\ge 2\),那么可以合法地减小 \(cntl\) 和 \(cntr\) 之间的数量差距,用 \(1\) 的代价移动并匹配即可。
注意特判奇数和 \(cntl-cntr<a[i]\) 的情况。
对于最后的局面,我们直接让左面袜子和右面袜子用 \(1\) 的代价更改颜色并匹配,计入答案为 \(\frac {( cntl + cntr )} 2\)。
如果 \(cntl\) 最后没有和 \(cntr\) 相等,那么还需要加上用于消除两边数量差距的 \(\frac {( cntl - cntr )} 2\)。
时间复杂度 \(O(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 lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 2e5 + 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 , l , r , a[N] , b[N] , ans;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int T = read();
while ( T -- )
{
n = read() , l = read() , r = read();
memset ( a , 0 , sizeof a );
memset ( b , 0 , sizeof b );
ans = 0;
for ( int i = 1 ; i <= l ; i ++ ) ++ a[read()];
for ( int i = l + 1 ; i <= n ; i ++ ) ++ b[read()];
for ( int i = 1 ; i <= n ; i ++ )
{
if ( a[i] >= b[i] ) a[i] -= b[i] , b[i] = 0;
else if ( a[i] < b[i] ) b[i] -= a[i] , a[i] = 0;
}
// for ( int i = 1 ; i <= n ; i ++ ) cout << a[i] << ' ' << b[i] << endl;
int cntl = 0 , cntr = 0;
for ( int i = 1 ; i <= n ; i ++ ) cntl += a[i] , cntr += b[i];
if ( cntl < cntr )
{
for ( int i = 1 ; i <= n ; i ++ ) swap ( a[i] , b[i] );
swap ( cntl , cntr );
}
for ( int i = 1 ; i <= n ; i ++ )
{
if ( a[i] >= 2 )
{
if ( a[i] > ( cntl - cntr ) ) ans += ( cntl - cntr ) / 2 , cntl = cntr;
else ans += a[i] / 2 , cntl -= a[i] / 2 * 2;
}
if ( cntl == cntr ) break;
}
if ( cntl == cntr ) cout << ans + ( cntl + cntr ) / 2 << endl;
else cout << ans + ( cntl + cntr ) / 2 + ( cntl - cntr ) / 2 << endl;
}
return 0;
}
Phoenix and Computers
一类很新的 \(dp\),维护的主要是一个一个的连通块。
设 \(f_{i,j}\) 表示放置 \(i\) 个元素,形成了 \(j\) 个块的方案数量。
有三类操作。
-
将某一个块的元素数量 \(+1\)
那么每一个块都有可能 \(+1\),所以 \(f_{i,j}=f_{i-1,j}\times j\)。
-
新增一个块
类似插空的方法,因为对于 \(f_{i-1,j-1}\),有 \(j-1\) 个块,那么显然有 \(j\) 个空位可以插入。
所以 \(f_{i,j}=f_{i-1,j-1}\times j\)。
-
合并两个块
类似第二种类操作 只不过我们从 \(j+1\) 个块向 \(j\) 个块进行推导。
显然有 \(f_{i,j}=f_{i-1,j+1}\times j\)。
这些是这类 \(dp\) 的基本操作。
对于本题,也是依照上面的操作来推导。
-
新增一个元素
可以发现有两种情况:
- 直接加入,此时 \(f_{i,j}=f_{i-1,j}\times j \times 2\)。
- 隔一个加入,这样又有一个会自动生成,此时 \(f_{i,j}=f_{i-2,j}\times j\times 2\)。
注意,下面的乘二都是因为一个块可以在左面或者右面加入。
-
新增一个块
直接加即可,\(f_{i,j}=f_{i-1,j-1}\times j\)。
-
合并两个块
也有两种情况:
- 在两个块中间隔着两个,这种情况可以任意在某一边去加一个元素,\(f_{i,j}=f_{i-2,j+1}\times j\times 2\)。
- 在两个块中间隔着三个,这种情况我们可以在中间加一个元素,\(f_{i,j}=f_{i-3,j+1}\times j\)
这样我们将上述五种转移统一起来就做完了。
#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 lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
#define int long long
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 , mod , f[N][N];
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , mod = read();
f[1][1] = 1;
for ( int i = 2 ; i <= n ; i ++ )
for ( int j = 1 ; j <= n ; j ++ )
{
if ( i > 1 ) f[i][j] += f[i-1][j-1] * j;
if ( i > 1 ) f[i][j] += f[i-1][j] * j * 2;
if ( i > 2 ) f[i][j] += f[i-2][j] * j * 2;
if ( i > 2 ) f[i][j] += f[i-2][j+1] * j * 2;
if ( i > 3 ) f[i][j] += f[i-3][j+1] * j;
f[i][j] %= mod;
}
cout << f[n][1] << endl;
return 0;
}
Phoenix and Earthquake
我们考虑一个很显然的结论:如果 \(\sum_{i=1}^n a_i<x\times (n-1)\),那么一定是无解的。
那么我们尝试证明:如果不满足上面这个条件的生成树一定有解。
可以用归纳证明:
- 如果 \(a_i\ge x\) 那么我们可以先将 \(a_i\) 和 \(fa_i\) 连边 这样就转化为了 \(n-1\) 个点的情况
- 如果 \(a_i<x\) 那么我们可以将这条边先行删除 留待最后再加 这样也转化为 \(n-1\) 个点的情况 并且因为目标式子右面减掉了 \(x\) 而左面减掉了一个 \(\le x\) 的 \(a_i\) 那么原式仍然符合条件
那么我们遍历图并按照上述操作维护操作序列即可
#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 lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
#define int long long
const int N = 3e5 + 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 , m , fa[N] , sum , a[N] , x , ans[N] , head , tail;
vector<pii> e[N];
inl void add ( int u , int v , int w ) { e[u].eb(v,w); }
struct Dsu
{
void init() { for ( int i = 1 ; i <= n ; i ++ ) fa[i] = i; }
int find ( int u ) { return fa[u] == u ? u : fa[u] = find(fa[u]); }
}D;
void dfs ( int u , int ff , int id )
{
for ( auto p : e[u] )
{
int v = p.fi , w = p.se;
if ( v ^ ff ) dfs ( v , u , w );
}
if ( u == 1 ) return;
if ( a[u] >= x ) a[ff] += a[u] - x , ans[++head] = id;
else ans[--tail] = id;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = tail = read() , m = read() , x = read();
D.init();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , sum += a[i];
for ( int i = 1 , u , v ; i <= m ; i ++ )
{
u = read() , v = read();
int fu = D.find(u) , fv = D.find(v);
if ( fu == fv ) continue;
fa[fv] = fu;
add ( u , v , i ) , add ( v , u , i );
}
for ( int i = 1 ; i <= n ; i ++ ) if ( D.find(i) != D.find(1) ) return cout << "NO" << endl , 0;
if ( sum < x * ( n - 1 ) ) return cout << "NO" << endl , 0;
dfs ( 1 , 0 , 0 ) , cout << "YES" << endl;
for ( int i = 1 ; i < n ; i ++ ) cout << ans[i] << endl;
return 0;
}

浙公网安备 33010602011771号