CF练习记录(Codeforces Round #774 (Div. 2))


这一套题目前两道都比较简单,直接贴上代码吧。
题目集链接

A

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200;
 
void solve()
{
    ll n,s;
    scanf("%lld%lld",&n,&s);
    n=n*n;
    printf("%lld\n",s/n);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        solve();
    }
    return 0;
}

B

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
ll a[maxn];
ll sum[maxn];
void solve()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%lld",&a[i]);
    }
    sort(a,a+n);
    sum[0]=a[0];
    for(int i=1;i<n;i++)
    {
        sum[i]=a[i]+sum[i-1];
    }
    if(n&1)
    {
        if(2*sum[n/2]<sum[n-1])
            printf("YES\n");
        else
            printf("NO\n");
    }
    else
    {
        if(sum[n/2-1]+sum[n/2]<sum[n-1])
            printf("YES\n");
        else
            printf("NO\n");
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        solve();
    }
    return 0;
}

C Factorials and Powers of Two

这道题的意思是在给你定义了一些数,这些数分别是2的幂次数和阶乘数,然后任意给定你一个小于等于1e12的数,然后从这些数中选出一些数的和刚好等于这个数,最后输出这些数的个数。

思路

因为我们知道这个个数是一定存在的,因为所有的数都可以表示为二进制,所以我们关注的焦点就到了阶乘数的部分,我们先预处理一下所有的阶乘数,我看了只有十二个,因此我们直接暴力搜索即可,判断某一个数选上或者选不上只需要一个二进制位来表示即可,每一次查询的复杂度不会超过4096。具体操作看代码。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200;
ll a[maxn];
int cnt=0;
ll min(ll aa,ll bb)
{
    return aa>bb?bb:aa;
}

ll get_bit1(ll n)
{
	return 1ll*__builtin_popcountll(n);//系统函数,返回二进制中1的个数
}

void init()
{
    ll base=2;
    for(int i=3;i<=14;i++)预处理阶乘数,这里从3开始处理,因为12也是2的幂次数,避免重复
    {
        a[cnt++]=1ll*base*i;
        base=a[cnt-1];
    }
}

void solve()
{
    ll n;
    scanf("%lld",&n);
    ll res=get_bit1(n);//先看一下这个数中有几个一,说明可以用几个2的幂次数表示,在这个答案的基础上更新
    int xia=upper_bound(a,a+cnt,n)-a;//从比当前数的小的数中开始暴力搜索
    int lon=1<<xia;
    for(int i=0;i<lon;i++)//根据每一个二进制位来表示是否选这一个数
    {
        ll ans=0;
        int ba=i;
        for(int j=0;j<xia;j++)
        {
            ans+=(a[j]*(ba&1));
            ba>>=1;
        }
        if(ans>n)//如果选出来的数的和比n还大,那肯定不符合要求。
            continue;
        res=min(res,get_bit1(1ll*i)+get_bit1(n-ans));//如果小于等于n的话,那就再加上二进制数即可,等于n的时候不用加数了
    }
    cout<<res<<endl;
}

int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        solve();
    }
    return 0;
}

D Weight the Tree

思路

这里盗用一下我队友的思路,链接
题目大意:给出有 n n n个节点的树,编号 1 1 1 n n n,定义好节点:邻接点点权和等于该点点权,现在要为树节点赋值,求好节点数量最大的赋值方案,如果多解,输出点权和最小的方案。
思路:由于权值和要最小化,那么不为好点直接设置为 1 1 1即可,除了只有两个点的特殊情况外,好点和坏点必然是交替的,那么好点周围必然全是坏点,点权值也就是多个 1 1 1相加,考虑 d p dp dp d p [ i ] [ 0 ] dp[i][0] dp[i][0] i i i不为好节点,dp[i][1]为好节点, a n s ans ans为该点及其子树中好点个数, s u m sum sum表示自己以及子树点权和,可得转移方程
d p [ u ] [ 1 ] . a n s + = d p [ v ] [ 0 ] . a n s dp[u][1].ans+=dp[v][0].ans dp[u][1].ans+=dp[v][0].ans
d p [ u ] [ 1 ] . s u m + = d p [ v ] [ 0 ] . s u m dp[u][1].sum+=dp[v][0].sum dp[u][1].sum+=dp[v][0].sum
d p [ u ] [ 0 ] . a n s + = m a x . a n s dp[u][0].ans+=max.ans dp[u][0].ans+=max.ans
d p [ u ] [ 0 ] . s u m + = m a x . s u m dp[u][0].sum+=max.sum dp[u][0].sum+=max.sum
max为好点个数最大且点权和最小的子节点。

代码

#include <bits/stdc++.h>
#define int long long
const int inf=1e13;
using namespace std;
const int maxn=2e5+5;
int t,n,cnt,head[maxn],degree[maxn],w[maxn];
struct node {
    int next,to;
} e[maxn<<1];
void Add(int from,int to) {
    e[++cnt].next=head[from];
    e[cnt].to=to;
    head[from]=cnt;
}
struct x {
    int ans,sum;//子节点内(包括自己)好点个数,子树和
    bool operator==(const x& a)const {
        return a.ans==ans&&a.sum==sum;
    }
} dp[maxn][2];
x getmax(x a,x b) {
    if(a.ans>b.ans)return a;
    if(a.ans==b.ans&&a.sum<b.sum)return a;
    return b;
}
void DFS(int u,int f) {
    dp[u][0]=(x) {//不是好点
        0,1ll
    };
    dp[u][1]=(x) {//是好点,那么周围的所有点都不为好点,点权为度
        1ll,degree[u]
    };
    for(int i=head[u]; ~i; i=e[i].next) {
        int v=e[i].to;
        if(v==f)continue;
        DFS(v,u);
        dp[u][1].ans+=dp[v][0].ans;
        //这个点设置为好点,那么邻接点不为好点
        dp[u][1].sum+=dp[v][0].sum;
        //统计子树点权和
        x t=getmax(dp[v][0],dp[v][1]);
        //如果这个点不是好点,选择好点多且子树和少的方案
        dp[u][0].ans+=t.ans;
        //统计好点
        dp[u][0].sum+=t.sum;
        //统计子树点权和
    }
}
void Print(int u,int f,bool flag) {
    w[u]=flag?degree[u]:1;//如果是好点,点权为度,否则为1
    for(int i=head[u]; ~i; i=e[i].next) {
        int v=e[i].to;
        if(v==f)continue;
        Print(v,u,flag?0:getmax(dp[v][0],dp[v][1])==dp[v][1]);
        //flag=1,是好点,那么邻接点v不为好点,否则判断v是不是好点
    }
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>n;
    memset(head,-1,sizeof(head));
    for(int i=1,u,v; i<n; i++) {//建图
        cin >>u>>v;
        Add(u,v);
        Add(v,u);
        degree[v]++,degree[u]++;//记录度
    }
    if(n==2) {//特判节点数为2
        cout <<"2 2\n1 1";
        return 0;
    }
    DFS(1,1);//深搜
    x t=getmax(dp[1][0],dp[1][1]);//获得最值
    Print(1,1,t==dp[1][1]);//t==dp[1][1]判断1是不是好点
    cout <<t.ans<<" "<<t.sum<<endl;
    for(int i=1; i<=n; i++)cout <<w[i]<<" ";
    return 0;
}

E power Board

这道题题意应该没什么问题,就是给了两个数 n n n m m m,分别表示 n n n m m m列,然后对于矩阵中的每一个元素 a [ i ] [ j ] a[i][j] a[i][j],元素的值都是 i j i^j ij。然后对于给定的 n n n m m m值我们要知道里面元素值的个数是多少个。

思路

最主要的就是理解清楚会重复的是什么元素,这是重中之重,这里我们就要看幂数的性质了。比如我们知道 2 4 = 2 2 2 = 4 2 2^4=2^{2^2}=4^2 24=222=42,那这样映射一下就是 3 4 = 3 2 2 = 9 2 3^4=3^{2^2}=9^2 34=322=92,所以这里我们就知道了对于一个 a 4 a^4 a4,一定有一个数b和一个数c,满足 a 4 = b 2 = c 1 a^4=b^2=c^1 a4=b2=c1。而我们的预处理就是针对的里面数中的幂数(也就是次数)。所以实际上的预处理就是预处理幂数,然后在后面添加数量的时候,我们直接

for(int i=1;i<=20;i++)
{
	for(int j=1;j<=m;j++) 
		cnt+=!book[i*j],book[i*j]=true;
	s[i]=cnt;
}

代码

#include <bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f
using namespace std;
const int maxn=2e7+10;
bool mark[maxn];//mark在init里面是标记幂数的
bool mark2[1000005];
ll s[30],cnt;
void init(int m)
{
    cnt=0;
    for(int i=1;i<=20;i++)
    {
        for(int j=1;j<=m;j++)
        {
        	if(!mark[i*j])
            	cnt++;
            mark[i*j]=true;
        }
        s[i]=cnt;
    }
}

void solve()
{
    int n,m;
    scanf("%d%d",&n,&m);
    init(m);
    ll ans=1;//因为第一行都是1,所以初始答案就是1。
    for(ll i=2;i<=n;i++)
    {
        if(mark2[i])
            continue;
        ll c=0;
        for(ll j=i;j<=n;j*=i)//这里一定要用ll,因为i和j相乘的话答案有可能会超int
            mark2[j]=true,c++;
        ans+=s[c];
    }
    printf("%lld\n",ans);
    return ;
}

int main()
{
    int t=1;
    //scanf("%d",&t);
    while(t--)
    {
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值