题解 | #优美的数#

优美的数

https://ac.nowcoder.com/acm/contest/20038/A

2021牛客OI赛前集训营-普及组(第一场)部分题解

传送门:https://ac.nowcoder.com/acm/contest/20038#question

A题:按题意,直接枚举即可,时间复杂度 O(n)。

#include<cstdio>
#include<iostream>
using namespace std;
const int N=3010;
int T,a[N],ans[N],m,cnt,num;
bool check(int n){
    if(n%7==0) return 1;
    else{
        while(n!=0){
            if(n%10==7) return 1;
            n/=10;
        }
        return 0;
    }
}
int main(){
    scanf("%d",&T);
    for(int i=1;i<=T;++i){
        scanf("%d",&a[i]);
        m=max(m,a[i]);
    }
    while(cnt<=m){
        ++num;
        if(check(num)) ans[++cnt]=num;
    }
    for(int i=1;i<=T;++i) printf("%d\n",ans[a[i]]);
    return 0;
}

B题:注意到异或的逆运算还是异或,即 a^b=X, 则 b=X^a, 即对于每一个 a[i],查找 X^a[i] 的个数,使用二分查找即可,时间复杂度 O(nlogn)。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long 
const int N=1e6+10;
int n,x,a[N],cnt;
signed main(){
    scanf("%lld%lld",&n,&x);
    for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;++i){
        int t=x^a[i],j1,j;
        if(a[i]==t) ++cnt;
        j1=lower_bound(a+i+1,a+n+1,t)-a,j=j1;
        if(j1>=1&&j1<=n) while(a[j]==t&&j!=i) ++cnt,++j;
        j1=lower_bound(a+1,a+i,t)-a,j=j1;
        if(j1>=1&&j1<=n) while(a[j]==t&j!=i) ++cnt,++j;
    }
    printf("%lld",cnt);
    return 0;
}

C题:

  1. 60分做法:ST表+暴力枚举,时间复杂度 O(n2),不能通过。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e7+3,LOG=29,MAX=0x3f3f3f3f;
int n,m,k,ans=MAX,a[N],b[N],f1[N][LOG],Log2[N],q[N],f2[N][LOG];
void init_log2(){
    Log2[1]=0,Log2[2]=1;
    for(int i=3;i<=n;++i) Log2[i]=Log2[i/2]+1;
}
void dp_len1(){
    for(int i=1;i<=n;++i) f1[i][0]=a[i];
    for(int j=1;j<=Log2[n];++j){
        int len=1<<j;
        for(int i=1;i<=n-len+1;++i) f1[i][j]=min(f1[i][j-1],f1[i+(1<<(j-1))][j-1]);     
    }
}
void dp_len2(){
    for(int i=1;i<=n;++i) f2[i][0]=b[i];
    for(int j=1;j<=Log2[n];++j){
        int len=1<<j;
        for(int i=1;i<=n-len+1;++i) f2[i][j]=max(f2[i][j-1],f2[i+(1<<(j-1))][j-1]);     
    }
}
int st_min1(int l,int r){
    int len=r-l+1;
    return min(f1[l][Log2[len]],f1[r-(1<<Log2[len])+1][Log2[len]]);
}
int st_max2(int l,int r){
    int len=r-l+1;
    return max(f2[l][Log2[len]],f2[r-(1<<Log2[len])+1][Log2[len]]);
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    for(int i=1;i<=n;++i) scanf("%d",&b[i]);
    init_log2();
    dp_len1();
    dp_len2();
    for(int l=1;l<=n;++l){
        for(int r=l;r<=n;++r){
            int t1=st_max2(l,r),t2=st_min1(l,r),len=r-l+1;
            if(t1-t2>=k) ans=min(ans,len);
  //          printf("%d %d:%d %d\n",l,r,t1,t2);
        }
    }
    if(ans==MAX) printf("So Sad!");
    else printf("%d",ans);
    return 0;
}

2.80分做法:在60分的基础上加上"二分答案"优化,时间复杂度 O(nlogn)。

3.正解:单调队列+双指针。每次移动一下移动右端点,然后移动左端点直到不符合题意,取个最小值;用单调队列维护区间最大 / 最小值。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e7+3,MAX=0x3f3f3f3f;
int n,m,k,ans=MAX,a[N],b[N],q1[N],q2[N],f1=1,r1,f2=1,r2;
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    for(int i=1;i<=n;++i) scanf("%d",&b[i]);
    q1[0]=1,q2[0]=1; 
    for(int i=1,j=1;i<=j&&j<=n;){
        while(f1<=r1&&a[j]<=a[q1[r1]]) --r1;
        q1[++r1]=j;
        while(f2<=r2&&b[j]>=b[q2[r2]]) --r2;
        q2[++r2]=j;
        while(i<=j&&b[q2[f2]]-a[q1[f1]]>=k){
               ans=min(ans,j-i+1);
               ++i;
               while(f1<=r1&&q1[f1]<i) ++f1;
               while(f2<=r2&&q2[f2]<i) ++f2;
          }
          ++j;
    }
    if(ans==MAX) printf("So Sad!");
    else printf("%d",ans);
    return 0;
}

D题:树形DP,为二次扫描与换根法的经典题,即为洛谷P1364“医院设置” ( https://www.luogu.com.cn/problem/P1364 ) 加强版。根据求和的性质以及乘法分配律,缩一个距离,做功就减少 Sum(i) 个;同样地,加一个距离,做功就增加 Sum(i) 个 [ Sum 为子树权值之和(包括自身)] ,所以动规方程即为 F(i) = F( Father(i) ) - Sum(i) + Sum( root ) - Sum(i) ,F 为做功,Father 为父节点,Root为根。存图要存双向,任选一个为根,一遍 DFS 扫描求 Sum 和 F( root ),再一遍 DFS 扫描求 F ,统计最大值即可。 注意:N是2e5,极大值MAX要开到 0x7f7f7f7f7f7f7f7f 。(亲身经历:100 pts -> 10 pts)

#include<cstdio>
#include<iostream>
using namespace std;
#define int long long
const int N=3e5+10,MAX=0x7f7f7f7f7f7f7f7f;
int n,w[N],cnt,sum[N],f[N],head[N<<1],ans=MAX,dep[N];
struct Node {
	int to,next;
} e[N<<1];
void Add(int u, int v) {
	++cnt;
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa) {
	sum[u]=w[u];
	for(int i=head[u]; i; i=e[i].next) {
		int s=e[i].to;
		if(s==fa) continue;
		dep[s]=dep[u]+1;
		dfs(s,u);
		sum[u]+=sum[s];
	}
	f[1] += w[u] * dep[u];
}
void dp(int u,int fa) {
	ans=min(ans,f[u]);
	for(int i=head[u]; i; i=e[i].next) {
		int s=e[i].to;
		if(s==fa) continue;
		f[s]=f[u]-sum[s]+sum[1]-sum[s];
		dp(s,u);
	}
}
signed main() {
	cin>>n;
	for(int i=1; i<=n; ++i) cin>>w[i];
	for(int i=1; i<=n-1; ++i) {
		int u, v;
		cin>>u>>v;
		Add(u,v);
		Add(v,u);
	}
	dfs(1,0);
	dp(1,0);
	cout<<ans<<endl;
	return 0;
}
全部评论

相关推荐

05-07 17:58
门头沟学院 Java
wuwuwuoow:1.简历字体有些怪怪的,用啥写的? 2.Redis 一主二从为什么能解决双写一致性? 3.乐观锁指的是 SQL 层面的库存判断?比如 stock > 0。个人认为这种不算乐观锁,更像是乐观锁的思想,写 SQL 避免不了悲观锁的 4.奖项证书如果不是 ACM,说实话没什么必要写 5.逻辑过期时间为什么能解决缓存击穿问题?逻辑过期指的是什么 其实也没什么多大要改的。海投吧
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
14
1
分享

创作者周榜

更多
牛客网
牛客企业服务