C. Factorials and Powers of Two
题意:
如果一个数为 2的幂次 或者是 阶乘数,那么定义这个数为漂亮数。
现在给定一个数 n,问最少可以由多少个不同的漂亮数构成?
1
≤
n
≤
1
0
12
1 ≤ n ≤ 10^{12}
1≤n≤1012
分析:
已知任何一个数都可以用若干个 2的幂次数 相加而成,2的那些幂次数 可以用将这个数化为二进制后,1的位置和个数确定。
观察到 n 的范围不大,在这个范围内的阶乘数一共14个,所以可以遍历这些数到底用了哪几个。(二进制枚举)
然后减去这些阶乘数之后,判断需要用多少2的幂次数构成。
答案 与 阶乘数+2的幂次数个数 取min。
二进制枚举:
一共 n 个数,判断拿了哪几个。
for(int i=0; i<(1<<n); i++){ //一共2^n次情况,状态压缩为一个数。
for(int j=0;j<n;j++){ //通过枚举这个数的二进制每一位来确定这个物品是否拿了
if(i & (1<<j)) ... //如果第j个位置为1,那么就拿了第j个位置。
}
}
完整Code:
#include<bits/stdc++.h>
using namespace std;
#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define int long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define endl '\n'
map<int,int> mp;
const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N];
int jc(int x){
int ans=1;
for(int i=1;i<=x;i++) ans*=i;
return ans;
}
signed main(){
Ios;
int cnt=0;n = 1000000000000;
for(int i=1;i<=n;i++)
{
int x = jc(i);
if(x > n) break;
a[++cnt] = x;
}
cin>>T;
while(T--)
{
cin>>n;
int ans = 1e9;
for(int i=0;i<(1ll<<14);i++)
{
int cnt=0, sum=0;
for(int j=0;j<14;j++)
{
if(i & (1<<j)) cnt++, sum+=a[j+1];
}
if(sum > n) break;
int t = n-sum;
for(int j=0;j<64;j++)
{
if(t & (1ll<<j)) cnt++;
}
ans = min(ans, cnt);
}
if(ans == 1e9) cout<<-1<<endl;
else cout<<ans<<endl;
}
return 0;
}
D. Weight the Tree
题意:
给出一个 n 个节点的树,定义一个点是好的:其点权为其相邻的所有点的点权之和。
现在请你将给这 n 个节点赋值,使得 好点 的个数尽量多。同时,在好点个数相同的情况下,使得 n 个节点点权之和尽可能小。
输出 好点 的最多个数,点权之和,和每个点的赋值。
分析:
- 当只有两个点的时候,两个点都可以作为好点;
- 当点数不止两个的时候,相邻的两点不能全是好点:对于相邻的两个点a, b,在这个树中,至少有一个点会与其他点相连,那么其相邻点就为另一个点加上其余的点。这样这两相邻的点就肯定不能满足都是好点。
对于第一种情况,特判即可。
下面讨论第二种情况:
那么对于一个点是好点的话,其相邻的点都一定不是好点。一个点不是好点,那么其相邻的点可能是好点,可能不是。
同时,需要保证最终的好点个数最多,在这个条件下点权之和最小。
这种情况下,可以考虑用树形dp。
dp[i, 0]
:点 i 不是好点时,以点 i 为根的子树,最多的好点个数 和 最小的点权之和。
dp[i, 1]
:点 i 是好点时,以点 i 为根的子树,最多的好点个数 和 最小的点权之和。
因为每个点要维护两个元素,所以可以用结构体定义:
struct node{
int gcnt, sum;
}dp[N][2];
当一个点为好点时,其值为相邻节点的权值之和,为了使得点权之和最小,所以相邻的节点,也就是不是好点的点权值全都赋值为1。那么好点的权值便是其相邻节点的个数。
所以对于一个点是好点的话,dp[i, 1]
的好点个数初始为 1,点权和初始为相邻点的个数;累加上其每个子节点的坏点状态时的好点个数和点权和;
不是好点的话,dp[i, 0]
的好点个数初始为 0,点权和初始为 1。累加上其每个子节点的两个状态中较好状态的好点个数和点权和。
如何比较两个状态呢?
当 好点个数较多 或者 好点个数相同的情况下点权和较小,则说明状态较好。
为了便于比较,可以重载大于号:
bool operator > (node a, node b){
if(a.gcnt != b.gcnt) return a.gcnt > b.gcnt;
return a.sum < b.sum;
}
从树形图的任意一个点出发,便可以得出整棵树的最大好点个数和最小点权和:
void dfs1(int u, int fa)
{
dp[u][1].gcnt = 1, dp[u][1].sum = e[u].size();
dp[u][0].gcnt = 0, dp[u][0].sum = 1;
for(auto tx:e[u])
{
if(tx == fa) continue;
dfs1(tx, u);
dp[u][1].gcnt += dp[tx][0].gcnt;
dp[u][1].sum += dp[tx][0].sum;
int better = (dp[tx][1] > dp[tx][0])?1:0;
dp[u][0].gcnt += dp[tx][better].gcnt;
dp[u][0].sum += dp[tx][better].sum;
}
}
这道题还要求输出在这个最佳情况下,每个点的赋值情况。所以我们按照最佳的路径重新遍历一遍。
void dfs2(int u, int state, int fa)
{
if(state) ans[u] = e[u].size();
else ans[u] = 1;
for(auto tx : e[u])
{
if(tx == fa) continue;
if(state) dfs2(tx, 0, u);
else{
int better = (dp[tx][1] > dp[tx][0])?1:0;
dfs2(tx, better, u);
}
}
}
完整Code:
#include<bits/stdc++.h>
using namespace std;
#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define int long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define endl '\n'
map<int,int> mp;
const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N], ans[N];
vector<int> e[N];
struct node{
int gcnt, sum;
}dp[N][2];
bool operator > (node a, node b){
if(a.gcnt != b.gcnt) return a.gcnt > b.gcnt;
return a.sum < b.sum;
}
void dfs1(int u, int fa)
{
dp[u][1].gcnt = 1, dp[u][1].sum = e[u].size();
dp[u][0].gcnt = 0, dp[u][0].sum = 1;
for(auto tx:e[u])
{
if(tx == fa) continue;
dfs1(tx, u);
dp[u][1].gcnt += dp[tx][0].gcnt;
dp[u][1].sum += dp[tx][0].sum;
int better = (dp[tx][1] > dp[tx][0])?1:0;
dp[u][0].gcnt += dp[tx][better].gcnt;
dp[u][0].sum += dp[tx][better].sum;
}
}
void dfs2(int u, int state, int fa)
{
if(state) ans[u] = e[u].size();
else ans[u] = 1;
for(auto tx : e[u])
{
if(tx == fa) continue;
if(state) dfs2(tx, 0, u);
else{
int better = (dp[tx][1] > dp[tx][0])?1:0;
dfs2(tx, better, u);
}
}
}
signed main(){
Ios;
cin>>n;
for(int i=1;i<n;i++)
{
int x,y;cin>>x>>y;
e[x].pb(y), e[y].pb(x);
}
if(n==2){
cout << 2 << " " << 2 <<endl << 1<<" "<<1<<endl;
return 0;
}
dfs1(1, 0);
int better = (dp[1][1]>dp[1][0])?1:0;
cout << dp[1][better].gcnt << " " << dp[1][better].sum << endl;
dfs2(1, better, 0);
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
return 0;
}