Codeforces Round 967 (Div. 2) A_E1

A.Make All Equal(思维)

题意:

给你一个循环数组a_1,a_2,…,a_na\_1,a\_2,\ldots,a\_na_1,a_2,,a_n

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

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

分析:

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

代码:

#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(构造)

题意:

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

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

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

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

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

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

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

分析:

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

我们假设排列为升序,那么正序需要操作一次,倒序需要操作nnn次,那么正序和倒序所需要的操作数就为n+1n+1n+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选择了一棵有nnn个节点、索引从111nnn的秘密树,并要求你使用以下类型的查询来猜测它:

  • “? a b”——Misuki会告诉你哪个节点xxx使∣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)是节点xxxyyy之间的距离。如果存在多个这样的节点,那么Misuki会告诉您哪个节点最小化了d(a,x)d(a,x)d(a,x)

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

分析:

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

代码:

#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_na\_1,a\_2,\ldots,a\_na_1,a_2,,a_n。设SSSaaa的所有可能的非空子序列的集合,且没有重复的元素。你的目标是找出SSS中最长的序列。如果有多个序列,请找出将奇数位置上的项乘以−1-11后,使词序最小的序列。

例如,给定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]

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

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

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

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

分析:

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

代码:

#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)(动态规划)

题意:

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

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

  1. 将变量vvv初始化为111

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

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

    2. a_xa\_xa_x赋值给a_va\_va_v(即a_v:=a_xa\_v:=a\_xa_v:=a_x);

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

  3. −1-11赋值给a_va\_va_v(即a_v:=−1a\_v:=-1a_v:=1)。

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

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

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

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

  • 选择一个整数vvv(1≤v≤2n−11\le v\le 2^n-11v2n1),并为111vvv之间路径上的每个顶点xxx添加111a_xa\_xa_x

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

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

分析:

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

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

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

C[x][i]=(x+2i−22i−2)=(x+2i−2x)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)

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

转移时枚举左子树根的值kkk,右子树值为j−kj−kjk,易得转移: dp[i][j]=∑_k=0且k≠j−kjdp[i−1][maxk,j−k]×C[mink,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]

进一步枚举maxk,j−kmax{k,j−k}maxk,jk可以得到更简洁的公式: dp[i][j]=∑_k=⌊j2⌋+1j×dp[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]

需要前缀和处理dp[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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值