Codeforces Round 967 (Div. 2) A_E1

A.Make All Equal(思维)

题意:

给你一个循环数组 a _ 1 , a _ 2 , … , a _ n a\_1,a\_2,\ldots,a\_n a_1,a_2,,a_n

你最多可以对 a a a执行 n − 1 n-1 n1次以下操作:每次操作中,你可以选择任意两个相邻的元素,并恰好删除其中一个元素。首、尾元素也视作两个相邻的元素。

你的目标是找出使 a a a中所有元素相等所需的最少运算次数。

分析:

观察题目,其实不难发现,保留一种数字之后,删除操作就可以删除任意其余数字。 找出这些数的最大出现次数,总数减去这个值即为答案。

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=100;
const int MOD=1000000007;

void solve(){
	map<int,int>mp;
	int maxn=0;
	int n;
	cin>>n;
	for(int i=1;i<=n;++i){
		int x;
		cin>>x;
		mp[x]++;
		maxn=max(maxn,mp[x]);
	} 
	cout<<n-maxn<<endl;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
  	int T;
	cin>>T;
  	while(T--){
		solve();
    }
    return 0;
}

B.Generate Permutation(构造)

题意:

有一个长度为 n n n的整数序列 a a a,其中每个元素的初始值为 − 1 -1 1

美雪有两台打字机,第一台打字机从左往右写字母,指针最初指向 1 1 1,另一台打字机从右往左写字母,指针最初指向 n n n

美雪会选择其中一台打字机进行以下操作,直到 a a a变成 [ 1 , 2 , … , n ] [1,2,\ldots,n] [1,2,,n]的排列。

  • 写数:将数组 a a a中不存在的最小正整数写入元素 a _ i a\_i a_i i i i是指针指向的位置。这种操作只有在 a _ i = − 1 a\_i=-1 a_i=1时才能执行。

  • 回车:将指针返回到初始位置(例如,第一台打字机为 1 1 1,第二台打字机为 n n n)。

  • 移动指针:将指针移动到下一个位置,让 i i i成为该操作前指针所指向的位置,如果美雪使用的是第一台打字机,则为 i : = i + 1 i:=i+1 i:=i+1,否则为 i : = i − 1 i:=i-1 i:=i1。只有在操作之后, 1 ≤ i ≤ n 1\le i\le n 1in成立时,才能执行此操作。

你的任务是构造长度为 n n n的任意排列 p p p,使得无论美雪使用哪台打字机, a = p a=p a=p所需的最小回车操作次数都相同。

分析:

观察测试样例,可以发现偶数长度无解。下面考虑奇数长度的情况。

我们假设排列为升序,那么正序需要操作一次,倒序需要操作 n n n次,那么正序和倒序所需要的操作数就为 n + 1 n+1 n+1次,因此需要做的就是让正序和倒序操作数相同。于是进行构造:先找出序列中位数,互换左右两边的数,交换完左边的数是倒着的,右边的数是正着的。

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=100;
const int MOD=1000000007;

void solve(){
	int n;
	cin>>n;
	if(n==1){
		cout<<1<<endl;
	} 
	else if(n%2==0) 
		cout<<-1<<endl;
	else{
		for(int i=n;i>n/2+1;--i)
			cout<<i<<" ";
		for(int i=1;i<=n/2+1;++i) 
			cout<<i<<" ";
		cout<<endl;
	}
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
  	int T;
	cin>>T;
  	while(T--){
		solve();
  }
    return 0;
}

C.Guess The Tree(搜索)

题意:

这是一个互动问题。

Misuki选择了一棵有 n n n个节点、索引从 1 1 1 n n n的秘密树,并要求你使用以下类型的查询来猜测它:

  • “? a b”——Misuki会告诉你哪个节点 x x x使 ∣ d ( a , x ) − d ( b , x ) ∣ |d(a,x)-d(b,x)| d(a,x)d(b,x)最小,其中 d ( x , y ) d(x,y) d(x,y)是节点 x x x y y y之间的距离。如果存在多个这样的节点,那么Misuki会告诉您哪个节点最小化了 d ( a , x ) d(a,x) d(a,x)

用最多 15 n 15n 15n次查询找出Misuki秘密树的结构!

分析:

分析每次询问得到的结果,可以发现每次都会给出 a , b a,b a,b路径上的中点,设为 m i d mid mid,那么我们继续询问 a , m i d a,mid a,mid m i d , b mid,b mid,b,直到 m i d = = a mid==a mid==a或者 m i d = = b mid==b mid==b。如果找到了答案,将他们存到 a n s ans ans即可。注意需要记录父节点以防重复询问。

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=1005;
const int MOD=1000000007;
LL fa[N];
vector<pair<LL,LL>>ans;

int ask(int a, int b){
    cout<<"? "<<a<<" "<<b<<endl;
    int tmp;
    cin>>tmp;
    return tmp;
}
 
void dfs(int a,int b){
    if (fa[a] && fa[b])
        return;
    int mid=ask(a, b);
    if(a==mid || mid==b){
        fa[b]=a;
        ans.push_back({a,b});
        return;
    }
    else{
        dfs(a,mid);
        dfs(mid,b);
    }
}
 
void solve(){
    int n;
    cin>>n;
    ans.clear();
    for(int i=0;i<=n;i++){
        fa[i]=0;
    }
    fa[1]=1;
    while(ans.size()<n-1){
        for(int i=2;i<=n;i++){
            if(fa[i]==0){
                dfs(1,i);
            }
        }
    }
    cout<<"! ";
    for(int i=0;i<ans.size();i++){
        cout<<ans[i].first<<" "<<ans[i].second<<" ";
    }
    cout<<endl;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
  	int T;
	cin>>T;
  	while(T--){
		solve();
  }
    return 0;
}

D.Longest Max Min Subsequence(贪心)

题意:

给你一个整数序列 a _ 1 , a _ 2 , … , a _ n a\_1,a\_2,\ldots,a\_n a_1,a_2,,a_n。设 S S S a a a的所有可能的非空子序列的集合,且没有重复的元素。你的目标是找出 S S S中最长的序列。如果有多个序列,请找出将奇数位置上的项乘以 − 1 -1 1后,使词序最小的序列。

例如,给定 a = [ 3 , 2 , 3 , 1 ] a=[3,2,3,1] a=[3,2,3,1]
S = [ 1 ] , [ 2 ] , [ 3 ] , [ 2 , 1 ] , [ 2 , 3 ] , [ 3 , 1 ] , [ 3 , 2 ] , [ 2 , 3 , 1 ] , [ 3 , 2 , 1 ] S={[1],[2],[3],[2,1],[2,3],[3,1],[3,2],[2,3,1],[3,2,1]} S=[1],[2],[3],[2,1],[2,3],[3,1],[3,2],[2,3,1],[3,2,1]。那么 [ 2 , 3 , 1 ] [2,3,1] [2,3,1] [ 3 , 2 , 1 ] [3,2,1] [3,2,1]将是最长的,而 [ 3 , 2 , 1 ] [3,2,1] [3,2,1]将是答案,因为 [ − 3 , 2 , − 1 ] [-3,2,-1] [3,2,1]的词序小于 [ − 2 , 3 , − 1 ] [-2,3,-1] [2,3,1]

如果 c c c可以从 d d d中删除几个(可能是零个或全部)元素而得到,那么序列 c c c就是序列 d d d的子序列。

当且仅当以下条件之一成立时,序列 c c c在词法上小于序列 d d d

  • c c c d d d的前缀,但 c ≠ d c\ne d c=d

  • c c c d d d不同的第一个位置,序列 c c c中的元素小于 d d d中的相应元素。

分析:

考虑贪心求解,假设要构建的序列为 b b b,长度为 m m m,对于 b _ i b\_i b_i,需要找到合适的范围 [ a , b ] [a,b] [a,b],使其满足对于 [ b + 1 , n ] [b+1,n] [b+1,n],序列 a a a中不同元素的数量为 m − i m-i mi个。为了解决这个问题,我们引入 l l l数组用于记录每个元素最后的坐标。这样可以将范围中的 b b b转化为 l _ 1 − l _ n l\_1-l\_n l_1l_n中的最小值。此外,观察发现 a a a b b b都是非严格递增。因此,可以借助优先队列或单调栈解决。

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL N = 5e5 + 5;
const LL INF = 1e15;
const int MOD=1000000007;

LL l[N],a[N],b[N];
bool vis[N];

struct Node{
    LL v,index;
    bool operator<(const Node &u) const {
        if (v!=u.v)
            return v<u.v;
        return index>u.index;
    }
};

void solve(){
    priority_queue<LL, vector<LL>, greater<LL>> lx;
    priority_queue<Node>mx,mi;
    LL n;
    cin>>n;
    for (LL i=1;i<=n;i++){
        l[i]=INF;
        vis[i]=0;
    }
    for(LL i=1;i<=n;i++){
        cin>>a[i];
        l[a[i]]=i;
    }
    for(LL i=1;i<=n;i++){
        lx.push(l[i]);
    }
    for(LL i=1;i<=lx.top();i++){
        mx.push({a[i],i});
        mi.push({-a[i],i});
    }
    LL tmp=1,cnt=0;
    while(!mi.empty()){
        if(!(cnt & 1)){
            b[++cnt]=mx.top().v;
            vis[b[cnt]]=1;
            tmp= mx.top().index + 1;
        }
        else{
            b[++cnt]=-mi.top().v;
            vis[b[cnt]]=1;
            tmp= mi.top().index + 1;
        }
        while (!lx.empty() && lx.top() != INF && vis[a[lx.top()]]){
            LL j=lx.top();
            lx.pop();
            for(LL k=j+1;k<=min(lx.top(),n);k++){
                mx.push({a[k],k});
                mi.push({-a[k],k});
            }
        }
        while(!mx.empty() && (vis[mx.top().v] || mx.top().index<tmp))
            mx.pop();
        while(!mi.empty() && (vis[-mi.top().v] || mi.top().index<tmp))
            mi.pop();
    }
    cout<<cnt<<endl;
    for(LL i=1;i<=cnt;i++){
        if(i==cnt)
            cout<<b[i];
        else
            cout<<b[i]<<" ";
    }
    cout<<endl;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
  	int T;
	cin>>T;
  	while(T--){
		solve();
  }
    return 0;
}

E1.Deterministic Heap (Easy Version)(动态规划)

题意:

考虑一棵完美二叉树,大小为 2 n − 1 2^n-1 2n1,节点编号为 1 1 1 2 n − 1 2^n-1 2n1,根节点为 1 1 1。对于每个顶点 v v v( 1 ≤ v ≤ 2 n − 1 − 1 1\le v\le 2^{n-1}-1 1v2n11),顶点 2 v 2v 2v是它的左子顶点,顶点 2 v + 1 2v+1 2v+1是它的右子顶点。每个节点 v v v也有一个值 a _ v a\_v a_v

定义操作 p o p \mathrm{pop} pop如下:

  1. 将变量 v v v初始化为 1 1 1

  2. 重复以下过程,直到顶点

    1. v v v的子顶点中,选择数值较大的一个,并将该顶点记为 x x x;如果它们的数值相等(即 a _ 2 v = a _ 2 v + 1 a\_{2v}=a\_{2v+1} a_2v=a_2v+1),则可以选择其中任意一个;

    2. a _ x a\_x a_x赋值给 a _ v a\_v a_v(即 a _ v : = a _ x a\_v:=a\_x a_v:=a_x);

    3. x x x赋值给 v v v(即 v : = x v:=x v:=x);

  3. − 1 -1 1赋值给 a _ v a\_v a_v(即 a _ v : = − 1 a\_v:=-1 a_v:=1)。

如果存在唯一的操作方法,我们就说 p o p \mathrm{pop} pop操作是确定的。换句话说,只要在它们之间进行选择, a _ 2 v ≠ a _ 2 v + 1 a\_{2v}\neq a\_{2v+1} a_2v=a_2v+1就会成立。

如果每个顶点 v v v 1 ≤ v ≤ 2 n − 1 − 1 1\le v\le 2^{n-1}-1 1v2n11)的 a _ v ≥ a _ 2 v a\_v\ge a\_{2v} a_va_2v a _ v ≥ a _ 2 v + 1 a\_v\ge a\_{2v+1} a_va_2v+1都成立,那么这棵二叉树就叫做最大堆。

如果 p o p \mathrm{pop} pop操作在第一次执行时对堆是确定的,那么最大堆就是确定的。

最初,每个顶点 v v v都有 a _ v : = 0 a\_v:=0 a_v:=0( 1 ≤ v ≤ 2 n − 1 1\le v\le 2^n-1 1v2n1),而你的目标是计算通过应用下面的 a d d \mathrm{add} add操作恰好 k k k次所产生的不同确定性最大堆的数量:

  • 选择一个整数 v v v( 1 ≤ v ≤ 2 n − 1 1\le v\le 2^n-1 1v2n1),并为 1 1 1 v v v之间路径上的每个顶点 x x x添加 1 1 1 a _ x a\_x a_x

如果两个堆中有一个节点的值不同,则认为这两个堆是不同的。

由于答案可能比较大,请输出对 p p p取模的结果。

分析:

题目要求为满二叉树。 设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示层数为 i i i的二叉树,根节点值为 j j j的确定性二叉树的个数(根节点的值即为这颗子树进行的加操作) 发现可以只在根节点操作,即两儿子的值的和 ≤ j ≤j j

先求两儿子的值的和恰好为 j j j的个数,再通过前缀和处理得到两儿子的值的和 ≤ j ≤j j的个数。记 C [ x ] [ i ] C[x][i] C[x][i]表示在层数为 i i i的二叉树中,根节点值为 x x x的个数,对于该树不必满足确定性。

2 i − 1 2^i−1 2i1个节点中加操作 x x x次,隔板法得

C [ x ] [ i ] = ( x + 2 i − 2 2 i − 2 ) = ( x + 2 i − 2 x ) C[x][i]= \begin{pmatrix} x+2^i−2 \\ 2^i−2\\ \end{pmatrix} = \begin{pmatrix} x+2^i−2 \\ x\\ \end{pmatrix} C[x][i]=(x+2i22i2)=(x+2i2x)

可以通过 x x x次乘法预处理 C [ x ] [ i ] C[x][i] C[x][i]

转移时枚举左子树根的值 k k k,右子树值为 j − k j−k jk,易得转移: d p [ i ] [ j ] = ∑ _ k = 0 且 k ≠ j − k j d p [ i − 1 ] [ m a x k , j − k ] × C [ m i n k , j − k ] [ i − 1 ] dp[i][j]=\sum\_{k=0且k\neq j-k}^{j} dp[i−1][max{k,j−k}]×C[min{k,j−k}][i−1] dp[i][j]=_k=0k=jkjdp[i1][maxk,jk]×C[mink,jk][i1]

进一步枚举 m a x k , j − k max{k,j−k} maxk,jk可以得到更简洁的公式: d p [ i ] [ j ] = ∑ _ k = ⌊ j 2 ⌋ + 1 j × d p [ i − 1 ] [ k ] × C [ j − k ] [ i − 1 ] dp[i][j]=\sum\_{k=\lfloor \frac{j}{2} \rfloor+1}^{j}×dp[i−1][k]×C[j−k][i−1] dp[i][j]=_k=2j+1j×dp[i1][k]×C[jk][i1]

需要前缀和处理 d p [ i ] [ j ] dp[i][j] dp[i][j]

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 505;
int n,K;
LL mod;
LL jc[N], inv[N];
LL C[N][N];
LL qpow(LL a, LL b, LL mod){
    LL ans = 1;
    while(b){
        if(b&1) 
            ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}

void init(){
    jc[1]=jc[0]=inv[1]=inv[0]=1;
    for(int i=2;i<=500;++i) 
        jc[i]=jc[i-1]*i%mod;
    inv[500]=qpow(jc[500],mod-2,mod);
    for(int i=499;i>=2;--i) 
        inv[i]=inv[i+1]*(i+1)%mod;
}

void getC(int x, int i){
    LL ans=inv[x];
    LL tmp=(qpow(2,i-1,mod)-2+mod)%mod;
    for(int i=1;i<=x;++i) 
        ans=ans*(tmp+i)%mod;
    C[x][i]=ans;
}

LL dp[N][N],pd[N][N];

void add(LL &a,LL b){
    a=(a+b>=mod)?(a+b-mod):(a+b); 
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
  	int T;
	cin>>T;
    while(T--){
        cin>>n>>K>>mod;
        init();
        for(int x=0;x<=K;++x)
            for(int i=2;i<=n;++i)
                getC(x,i);

        for(int j=0;j<=K;++j) 
            dp[1][j]=1;
        for(int i=2;i<=n;++i){
            for(int j=1;j<=K;++j){
                dp[i][j]=pd[i][j]=0;
                for(int k=0;k<=j;++k){
                    if(k==j-k) 
                        continue;
                    add(pd[i][j],dp[i-1][max(k,j-k)]*C[min(k,j-k)][i]%mod);
                }
                add(pd[i][j],pd[i][j-1]);
                add(dp[i][j],pd[i][j]);
            }
        }
        cout<<dp[n][K]<<endl;
    }

    return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

抱歉,根据提供的引用内容,我无法理解你具体想要问什么问题。请提供更清晰明确的问题,我将竭诚为你解答。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Codeforces Round 860 (Div. 2)题解](https://blog.csdn.net/qq_60653991/article/details/129802687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【CodeforcesCodeforces Round 865 (Div. 2) (补赛)](https://blog.csdn.net/t_mod/article/details/130104033)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Codeforces Round 872 (Div. 2)(前三道](https://blog.csdn.net/qq_68286180/article/details/130570952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值