CF1515

CF1515

Phoenix and Gold

非常好随机化 爱来自 random_shuffle()

显然如果我们随机一下序列,有概率让整个数组满足这种情况的。

实测随机 6 次是最小随机化次数。

下面是正解:

一个很玄学的思路,我们从前往后扫一遍数组,遇到前缀和不合法的,就交换 aian,因为这两个数题目中保证了一定不相同,所以可以起到更改前缀和的作用。

如果到最后前缀和不能为 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 个塔,从前往后扫一遍序列,对于每一个数,我们贪心地选取它在当前高度最低的塔上即可。

因为保证了每一个数都 x,那么序列中的每两个塔之间的差值都 x

那么为最低的塔高 val 加上一个 x 的值 k,此时该塔高变为 val+k,设此时的最小塔高为 val,那么 valvalkx 那么一定有 val+kvalx,所以我们保证了按照我们的贪心策略,一定可以让序列中的每两个塔之间的差值都 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 的代价消除 cntlcntr 之间的数量差距。

只要左面点有袜子的数量 2,那么可以合法地减小 cntlcntr 之间的数量差距,用 1 的代价移动并匹配即可。

注意特判奇数和 cntlcntr<a[i] 的情况。

对于最后的局面,我们直接让左面袜子和右面袜子用 1 的代价更改颜色并匹配,计入答案为 (cntl+cntr)2

如果 cntl 最后没有和 cntr 相等,那么还需要加上用于消除两边数量差距的 (cntlcntr)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,维护的主要是一个一个的连通块。

fi,j 表示放置 i 个元素,形成了 j 个块的方案数量。

有三类操作。

  1. 将某一个块的元素数量 +1

    那么每一个块都有可能 +1,所以 fi,j=fi1,j×j

  2. 新增一个块

    类似插空的方法,因为对于 fi1,j1,有 j1 个块,那么显然有 j 个空位可以插入。

    所以 fi,j=fi1,j1×j

  3. 合并两个块

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

    显然有 fi,j=fi1,j+1×j

这些是这类 dp 的基本操作。

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

  1. 新增一个元素

    可以发现有两种情况:

    • 直接加入,此时 fi,j=fi1,j×j×2
    • 隔一个加入,这样又有一个会自动生成,此时 fi,j=fi2,j×j×2

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

  2. 新增一个块

    直接加即可,fi,j=fi1,j1×j

  3. 合并两个块

    也有两种情况:

    • 在两个块中间隔着两个,这种情况可以任意在某一边去加一个元素,fi,j=fi2,j+1×j×2
    • 在两个块中间隔着三个,这种情况我们可以在中间加一个元素,fi,j=fi3,j+1×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

我们考虑一个很显然的结论:如果 i=1nai<x×(n1),那么一定是无解的。

那么我们尝试证明:如果不满足上面这个条件的生成树一定有解。

可以用归纳证明:

  • 如果 aix 那么我们可以先将 aifai 连边 这样就转化为了 n1 个点的情况
  • 如果 ai<x 那么我们可以将这条边先行删除 留待最后再加 这样也转化为 n1 个点的情况 并且因为目标式子右面减掉了 x 而左面减掉了一个 xai 那么原式仍然符合条件

那么我们遍历图并按照上述操作维护操作序列即可

#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 @   Echo_Long  阅读(11)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示