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\)

    那么每一个块都有可能 \(+1\),所以 \(f_{i,j}=f_{i-1,j}\times j\)

  2. 新增一个块

    类似插空的方法,因为对于 \(f_{i-1,j-1}\),有 \(j-1\) 个块,那么显然有 \(j\) 个空位可以插入。

    所以 \(f_{i,j}=f_{i-1,j-1}\times j\)

  3. 合并两个块

    类似第二种类操作 只不过我们从 \(j+1\) 个块向 \(j\) 个块进行推导。

    显然有 \(f_{i,j}=f_{i-1,j+1}\times j\)

这些是这类 \(dp\) 的基本操作。

对于本题,也是依照上面的操作来推导。

  1. 新增一个元素

    可以发现有两种情况:

    • 直接加入,此时 \(f_{i,j}=f_{i-1,j}\times j \times 2\)
    • 隔一个加入,这样又有一个会自动生成,此时 \(f_{i,j}=f_{i-2,j}\times j\times 2\)

    注意,下面的乘二都是因为一个块可以在左面或者右面加入。

  2. 新增一个块

    直接加即可,\(f_{i,j}=f_{i-1,j-1}\times j\)

  3. 合并两个块

    也有两种情况:

    • 在两个块中间隔着两个,这种情况可以任意在某一边去加一个元素,\(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;
}
posted @ 2023-11-03 16:11  Echo_Long  阅读(17)  评论(2)    收藏  举报