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开始处理,因为1和2也是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;
}