CodeForces-Div2-678(A-D)
比赛情况
退役复健之CF,第一场VP,果然和想象中一样,勉强过了C,D赛后看了一下题解思路不太对,整体来说还是很符合rating 1300的水平的。
题解
A
题意的复杂公式转化后就是判断给定的数组和是否等于m。
B
给定n(n<=100),构造一个数的方阵满足每个数都不是素数,且每一行每一列的和都为素数。
我的做法是先筛素数,然后暴力构造,从n=3的全1矩阵开始,然后行和列对称,维护每一行的和,然后直接暴力枚举每个数判断新的一行的和是否为素数,最后,所有新添加的数的和也就是新的一行的和,通过这个和,就就构造最后的一个a[n-1][n-1]。
……突然发现题意看错,矩阵的数可以是0,所以按照题解的做法,直接根据n的奇偶性,如果n是偶数,则主副对角线都为1,其他为0,如果n是奇数,在偶数的基础上再补充两个1即可。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+50;
int t,n;
bool isPrime[N];
int ans[105][105],sum[105];
void init(){
isPrime[2]=true;
for(int i=3;i<N;i++){
isPrime[i]=true;
for(int j=2;j*j<=i;j++){
if(i%j==0){
isPrime[i]=false;
break;
}
}
}
}
int main(){
init();
scanf("%d",&t);
while(t--){
scanf("%d",&n);
if(n==1){
printf("1\n");
}else if(n==2){
printf("1 1\n1 1\n");
}else if(n==3){
printf("1 1 1\n1 1 1\n1 1 1\n");
}else{
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
ans[i][j]=1;
}
sum[i]=3;
}
for(int i=3;i<n;i++){
int shu=0;
for(int j=0;j<i;j++){
int nw=1;
while(!isPrime[nw+sum[j]]){
nw++;
while(isPrime[nw]){
nw++;
}
}
ans[i][j]=ans[j][i]=nw;
shu+=nw;
sum[j]+=nw;
}
int nw=1;
while(!isPrime[nw+shu]){
nw++;
while(isPrime[nw]){
nw++;
}
}
ans[i][i]=nw;
sum[i]=shu+nw;
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%d%c",ans[i][j],j==n-1?'\n':' ');
}
}
}
}
return 0;
}
C
给定n,x和p,求有多少个长为n的全排列满足p位置是x,且可以通过给定的二分查找算法查找到x。
按照给定的算法二分查找,因为二分的路径是一定的,所以可以确定每次二分定位的那个数要大于x还是小于x,还是等于x,所以最后可以得到的信息,就是这个全排列里,有多少个位置必须放比x大的数,多少个位置必须放比x小的数,剩下就随便放,排列数乘一下就可以了。
比赛时想得很乱,写完完全没底,结果直接秒过???
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+50;
const int MOD=1e9+7;
int n,x,p;
ll Pow(ll x,ll n,ll mod){
ll ans=1LL;
while(n){
if(n%2){
ans=ans*x%mod;
}
x=x*x%mod;
n/=2;
}
return ans;
}
ll Inv(ll x,ll mod){
return Pow(x,mod-2,mod);
}
ll fac[1005];
void init(){
fac[0]=1;
fac[1]=1;
for(ll i=2;i<1005;i++){
fac[i]=(fac[i-1]*i)%MOD;
}
}
ll A(int a,int b){
return (fac[a]*Inv(fac[a-b],MOD))%MOD;
}
int main(){
// freopen("in.txt","r",stdin);
init();
scanf("%d%d%d",&n,&x,&p);
int l=0,r=n;
int xiao=0;
int da=0;
while(l<r){
int mid=(l+r)/2;
if(mid<p){
l=mid+1;
xiao++;
}else if(mid==p){
l=mid+1;
}else{
r=mid;
da++;
}
}#include <bits/stdc++.h>
using namespace std;
const int N=1e5+50;
int t,n;
bool isPrime[N];
int ans[105][105],sum[105];
void init(){
isPrime[2]=true;
for(int i=3;i<N;i++){
isPrime[i]=true;
for(int j=2;j*j<=i;j++){
if(i%j==0){
isPrime[i]=false;
break;
}
}
}
}
int main(){
init();
scanf("%d",&t);
while(t--){
scanf("%d",&n);
if(n==1){
printf("1\n");
}else if(n==2){
printf("1 1\n1 1\n");
}else if(n==3){
printf("1 1 1\n1 1 1\n1 1 1\n");
}else{
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
ans[i][j]=1;
}
sum[i]=3;
}
for(int i=3;i<n;i++){
int shu=0;
for(int j=0;j<i;j++){
int nw=1;
while(!isPrime[nw+sum[j]]){
nw++;
while(isPrime[nw]){
nw++;
}
}
ans[i][j]=ans[j][i]=nw;
shu+=nw;
sum[j]+=nw;
}
int nw=1;
while(!isPrime[nw+shu]){
nw++;
while(isPrime[nw]){
nw++;
}
}
ans[i][i]=nw;
sum[i]=shu+nw;
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%d%c",ans[i][j],j==n-1?'\n':' ');
}
}
}
}
return 0;
}
D
一个只含单向边的有根树,根为1,每个节点有ai个居民,一开始绑匪在根节点,居民可以分散跑,可以在某个节点合并,居民先跑1步,然后绑匪再跑1步,两者都用最优策略,直到叶子节点居民无法再跑,问绑匪能抓到多少个居民。
比赛时想复杂了,其实对于任意一个子树,居民肯定要跑得越分散越好,所以就是sum[u]/leaf[u]向上取整即可,sum[u]表示子树u所有居民总数,leaf[u]表示子树u叶子总数。
注意: 假如子树的sum并不能全部分散到叶子,比如所有居民在一个分支,而无法向上走到另一个分支,这会导致对于这个子树的答案出错,但并不会导致总体的答案出错,因为最终dfs会走到这个分支对应的子树,而总体的答案是不断取max更新。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+05;
vector<int> g[N];
int n,f;
ll a[N];
ll sum[N],leaf[N];
ll ans=0;
void dfs(int u){
int siz=g[u].size();
sum[u]=a[u];
leaf[u]=siz==0;
for(int i=0;i<siz;i++){
int v=g[u][i];
dfs(v);
sum[u]+=sum[v];
leaf[u]+=leaf[v];
}
ans=max(ans,(sum[u]+leaf[u]-1)/leaf[u]);
}
int main(){
// freopen("in.txt","r",stdin);
scanf("%d",&n);
for(int i=2;i<=n;i++){
scanf("%d",&f);
g[f].push_back(i);
}
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
dfs(1);
printf("%lld\n",ans);
return 0;
}