Codeforces Round #774 (Div. 2)
A
有点诈骗的感觉,其实可以发现 \(n^2\) 和 \(n\) 根本不在同一个数量级上,所以答案就是 \(\dfrac{s}{n^2}\)。
$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
void solve(){
int n,s;
cin>>n>>s;
cout<<s/n/n<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;)
solve();
return 0;
}
B
题意比较绕,所以写慢了。考虑红色的和要大于蓝色但是红色的个数要小于蓝色。那么红色肯定是取大的数,蓝色一定是取小的数。贪心地构造,对数组排个序,然后红色从后往前取,蓝色从前往后取,并且红色的个数比蓝色少一,然后一格一格向中间并拢,容易证明一定是最优的策略。
$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=2e5+10;
int a[MAXN];
void solve(){
int n;cin>>n;
rep(i,1,n) cin>>a[i];
sort(a+1,a+1+n);
int mid=(2+n)>>1;
if(n%2==0) mid--;
int sr=0,sb=a[1];
rep(i,2,mid){
sb+=a[i],sr+=a[n-i+2];
if(sr>sb){
cout<<"YES\n";
return;
}
}cout<<"NO\n";
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;for(cin>>T;T--;)
solve();
return 0;
}
C
首先如果去掉用阶乘来表示,那么容易想到 \(popcount\) 就是这个数用二进制最少的表示方法。加上阶乘呢?可以发现,不超过 \(10^{12}\) 的阶乘数是不超过 \(15\) 个的。于是我们 \(2^{15}\) 暴力枚举每个阶乘选不选,然后减去这个阶乘后就求它的 \(popcount\) 作为当前答案。
$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
int fac[16];
int popcnt(int x){
int ret=0;
while(x) ret+=(x&1),x>>=1;
return ret;
}
void solve(){
int n,ans=INF;cin>>n;
rep(i,0,(1<<15)-1){
int sum=0,cnt=0;
rep(j,0,14) if(i&(1<<j))
sum+=fac[j],cnt++;
if(sum>n) continue;
ans=min(ans,popcnt(n-sum)+cnt);
}cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
fac[0]=1;rep(i,1,15) fac[i]=fac[i-1]*i;
int T;for(cin>>T;T--;)
solve();
return 0;
}
D
考虑到好点个数是第一关键字,所以先想想怎么求出最多的好点数。很容易想到一点就是除去 \(n=2\) 的情况,相邻的两个点不可能同时为好点,因为如果说 \(u,v\) 同时是好点,那么我们令和 \(u\) 相邻的不是 \(v\) 的节点权值和为 \(sum_u\),同理命 \(sum_v\)。那么就有 \(val_v+sum_u=val_u\),\(val_u+sum_v=val_v\)。那么这个等式成立当且仅当 \(sum_v=sum_u=0\)。
这个限制就很像没有上司的舞会了,那我们直接跑树形 dp。
然后考虑构造这个最小的权值和。首先如果不是好点权值一定是 \(1\),那么好点的权值就是与之相邻的点数。既然要考虑到这个权值,那么就产生了一个问题,如果树形 dp 的时候,儿子是不是好点所得的好点数相同的话,那么还要进一步判断它的权值和,取小的那个。
于是我们令 \(dp[i][0/1]\) 表示 \(i\) 是不是好点的情况下子树中最多的好点数以及 \(sum[i][0/1]\) 表示 \(i\) 是不是好点的情况下子树中最小的权值和(在好点最多的条件下)。
直接跑一遍,然后构造一下就行了。
$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=2e5+10;
vector<int> e[MAXN];
int dp[MAXN][2],sum[MAXN][2];
vector<pii> from[MAXN][2];
void dfs(int x,int fa){
dp[x][0]=0;dp[x][1]=1;
sum[x][0]=1;sum[x][1]=e[x].size();
for(int s:e[x]){
if(s==fa) continue;
dfs(s,x);
dp[x][1]+=dp[s][0];
sum[x][1]+=sum[s][0];
from[x][1].pb(mkp(s,0));
if(dp[s][0]>dp[s][1]){
dp[x][0]+=dp[s][0];
sum[x][0]+=sum[s][0];
from[x][0].pb(mkp(s,0));
}else if(dp[s][0]<dp[s][1]){
dp[x][0]+=dp[s][1];
sum[x][0]+=sum[s][1];
from[x][0].pb(mkp(s,1));
}else{
if(sum[s][0]<sum[s][1]){
dp[x][0]+=dp[s][0];
sum[x][0]+=sum[s][0];
from[x][0].pb(mkp(s,0));
}else{
dp[x][0]+=dp[s][1];
sum[x][0]+=sum[s][1];
from[x][0].pb(mkp(s,1));
}
}
}
}
int num[MAXN];
int cfs(int x,int chs){
int ret=(chs?e[x].size():1);
num[x]=(chs?e[x].size():1);
for(auto s:from[x][chs]){
ret+=cfs(s.fi,s.se);
}return ret;
}
int n;
void print(int chs){
int rubbb=cfs(1,chs);
cout<<dp[1][chs]<<' '<<rubbb<<'\n';
rep(i,1,n) cout<<num[i]<<' ';
cout<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
rep(i,2,n){
int u,v;cin>>u>>v;
e[u].pb(v);e[v].pb(u);
}
if(n==2){
cout<<"2 2\n";
cout<<"1 1\n";
return 0;
}
dfs(1,1);
if(dp[1][0]>dp[1][1]) print(0);
else if(dp[1][0]<dp[1][1]) print(1);
else{
if(sum[1][0]<sum[1][1]) print(0);
else print(1);
}
return 0;
}
E
考虑进行一个打表。首先如果有 \(a^b=c^d\),那么一定有 \(a\) 和 \(b\) 是同一个数的整次幂。于是我们依次考虑每个数,以 \(2\) 为例,我们把打出的表中 \(2\) 的整次幂爬出来看看。
2 4 8 16 32 ...
4 16 64 256 1024 ...
8 64 ...
用他们的次数来代表它:
1 2 3 4 5 ...
2 4 6 8 10 ...
3 6 9 ...
这不是乘法表吗。实际上无论对于任何一个数,我们把它写出来后都是这个样子的。那么两个数相等等价于其指数相等。然后我们对这张乘法表预处理一下到某一行为止,有多少个不同的数,用一个 bitset 维护一下就可以了。
最后做的时候,就直接枚举每个数,然后直接加上上面预处理出来的不同的数的个数就行了。
$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=1e6+10;
const int LOGB=20;
int a[MAXN];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,m,ans=1;
cin>>n>>m;
bitset<MAXN*LOGB> ppc;
rep(i,1,LOGB){
rep(j,1,m) ppc[i*j]=1;
a[i]=ppc.count();
}
bitset<MAXN*LOGB> vis;
rep(i,2,n){
if(vis[i]) continue;
int cur=1; vis[i]=1;
for(int j=i*i;j<=n;j*=i)
vis[j]=1,cur++;
ans+=a[cur];
}cout<<ans<<'\n';
return 0;
}