ACM集训队每周程序设计竞赛(1)题解

A-等差数列

将三个数从小到大排序再判断。

#include <iostream>
#include <algorithm>
using namespace std;

int main()
{
    int A[3];
    cin >> A[0];
    cin >> A[1];
    cin >> A[2];

    sort(A, A + 3);

    if (A[2] - A[1] == A[1] - A[0])
        cout << "Yes";
    else
        cout << "No";
    return 0;
}

B-次高山峰

简单模拟,找次大值即可。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
int n,t,t1,t2;
string s,s1,s2;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
	cin>>n;
    while(n--){
        cin>>s>>t;
        if(t>t1){
            s2=s1;t2=t1;
            s1=s;t1=t;
        }
        else if(t<t1&&t>t2){
            s2=s;t2=t;
        }
    }
    cout<<s2<<endl;
    return 0;
}

C-简单计数问题

问题关键在于对于数组A中的数,快速找到有多少个j满足。可以记录一个数组cnt,输入C数组中的数x时,cnt[b[x]]++,代表对于b[x],可以找到cnt[b[x]]j使其相等。那么答案就是cnt[a[i]]的和。

#include<bits/stdc++.h>
typedef long long ll; 
using namespace std;
const int N=1e5+10;
int a[N],b[N],cnt[N],ans;
int n;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i];
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		cnt[b[x]]++;
	}
	for(int i=1;i<=n;i++){
		ans+=cnt[a[i]];
	}
	cout<<ans<<endl;
	return 0;
}

D-简单字符串

从第一位开始枚举每一位,由于有a个a和b个b,那么第一位时a的有\binom{a+b-1}{a-1}个数,在枚举的同时,a的值与b的值同样在变化,同理第二位为a的有\binom{a+b-2}{a-1}。通过判断值的大小来确定当前位为a或b。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=110;
ll C[N][N];

void get_C(int n) {
    C[0][0] = 1;
    for(int i = 1; i <= n; i++) {
        C[i][0] = 1;
        for(int j = 1; j <= i; j++)
            C[i][j]= C[i - 1][j] + C[i - 1][j - 1];
    }
}

int main(){
    int a,b;
    ll k;
    cin>>a>>b>>k;
    get_C(60);
    int n=a+b;
    for(int i=1; i<=n; i++) {
        ll now = C[n-i][a - 1];
        if(a==0)now=0;
        if(k > now) k -= now, putchar('b'), --b;
        else putchar('a'), --a;
    }
    return 0;
}

E-简单异或问题

考虑异或的性质,a xor b = a xor c xor b xor c。

那么f[i]记为1-i路径的异或。答案为\sum_{i=1}^{n} \sum_{j=i+1}^{n}f[i] xor f[j]。我们按位来讨论贡献。

num0[j],num1[j]分别为f[i]二进制下0/1的个数。那么第j位的贡献位2^j \times num0[j] \times num1[j],将每一位求和即可。

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N=2e5+5,mod=1e9+7;
int n,head[N],cnt;
struct node{
	int next,to,w;
}e[N<<1];

void add(int from,int to,int w){
	e[++cnt]=(node){head[from],to,w};
	head[from]=cnt;
}

int f[N],num0[70],num1[70];

void dfs(int x,int fa){
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].to;
		if(y==fa)continue;
		f[y]=f[x]^e[i].w;
		dfs(y,x);
	}
}

signed main(){
	n=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read(),w=read();
		add(u,v,w),add(v,u,w);
	}
	dfs(1,0);
	for(int i=1;i<=n;++i){
		for(int j=0;j<60;++j){
			if(f[i]&(1ll<<j))num1[j]++;
			else num0[j]++;
		}
	}
	int ans=0;
	for(int i=0;i<60;++i){
		int tmp=num0[i]*num1[i]%mod;
		ans=(ans+(1ll<<i)%mod*tmp%mod)%mod;
	}
	print(ans);
	return 0;
}

F-新版移球游戏

很明显能看出是个dp问题。对于每一个球,有四种状态,去任意一个位置,最左边,最右边,不移动。对于不移动的球,它很有特殊性。可以围绕不动的球来设计状态。如果记不动的球为T= \left\{ t_1,t_2,...t_n \right\},表示t_i不进行操作。

那么对于1 \le j <t_1 最小代价min(a_j,b_j)

对于t_i < j < t_{i+1}代价为a_j

对于t_k<j \le n代价为min(a_j,c_j)

f[i]为将1-i已经排好,且i位置不使用操作的最小代价,pos[i]为编号为i的初始在的位置。i需要从它左边的j转移过来。考虑从f[j]转移到f[i],那么f[i]=min(\sum_{k=1}^{i-1}min(a_k,b_k),f[j]+\sum_{k=j+1}^{i-1}a_k)1 \le j <i,pos[j]<pos[i]

最终答案为min(f[i],\sum_{k=i+1}^{n}min(a_k,c_k))

为了方便转移,计算时记录的前缀最小值额外加上了\sum_{k=i}^{n}a_k

转移时可用树状数组维护前缀最小值。

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N=2e5+5;
int n,pos[N],a[N],b[N],c[N];
int suma[N],pre[N],suf[N],f[N],bit[N];

void add(int x,int y){
	for(int i=x;i<=n;i+=(i&-i)){
		bit[i]=min(bit[i],y);
	}
}

int query(int x){
	int ans=1e18;
	for(int i=x;i>=1;i-=(i&-i)){
		ans=min(ans,bit[i]);
	}
	return ans;
}

signed main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		int x;
		cin>>x;
		pos[x]=i,bit[i]=1e18;
	}
	for(int i=1;i<=n;++i){
		cin>>a[i]>>b[i]>>c[i];
		suma[i]=suma[i-1]+a[i];
		pre[i]=pre[i-1]+min(a[i],b[i]);
	}
	for(int i=n;i>=1;--i)suf[i]=suf[i+1]+min(a[i],c[i]);
	int ans=1e18;
	for(int i=1;i<=n;++i){
		f[i]=min(pre[i-1],query(pos[i])-(suma[n]-suma[i-1]));
		ans=min(ans,f[i]+suf[i+1]);
		add(pos[i],f[i]+suma[n]-suma[i]);
	}
	cout<<ans<<endl;
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值