牛客算法周周练15 ABCD题总结

题目地址:牛客周练15

A题:
在这里插入图片描述
目前我只知道两种方法,第一种,直接暴力,居然能过,代码如下(不做过多解释):

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 10000+8;

int Ai[maxn];
int B[maxn];

int main()
{
	int n;
	scanf("%d",&n);
	memset(Ai,0,sizeof(Ai));
	memset(B,0,sizeof(B));
	for(int i = 1; i <= n; i++)
	{
		scanf("%d",&Ai[i]);
	}
	for(int t = 1; t <= n; t++)
	{
		bool flag = false;
		for(int j = t+1; j <= n; j++)
		{
			if(Ai[j] > Ai[t])
			{
				B[t] = j;
				flag = true;
				break;
			}
		}
		if(!flag) B[t] = 0;
	}
	
	for(int cnt = 1; cnt <= n; cnt++)
	{
		if(cnt == 1) printf("%d",B[cnt]);
		else printf(" %d",B[cnt]);
	}
	printf("\n");
	return 0;
}

我们正常比赛的时候第一种方法我个人感觉可能过不了,那么试试第二种。
第二种方法就是构建一个线段树,树根是区间内最大的值,然后依次查找。

线段树解法:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+8;

int T,ans;
int a[maxn] = {0},B[maxn] = {0};
struct Tree
{
	int l,r,val,id;
}Tree[maxn*4];

void build(int k,int ll,int rr)//id记录标号,val大小
{
	Tree[k].l = ll,Tree[k].r = rr;
	
	if(Tree[k].l == Tree[k].r)
	{
		int s = ll;
		Tree[k].id = s;
		Tree[k].val = a[s];
		return;
	}
	int mid = (ll+rr)>>1;
	build(k<<1,ll,mid);
	build(k<<1|1,mid+1,rr);
	
	if(Tree[k<<1].val > Tree[k<<1|1].val)
	{
		Tree[k].val = Tree[k<<1].val;
		Tree[k].id = Tree[k<<1].id;
	}
	else
	{
		Tree[k].val = Tree[k<<1|1].val;
		Tree[k].id = Tree[k<<1|1].id;
	}
}

int query(int k,int ll,int rr,int res)//查找时在所有的可能值中找最小的
{
	if(Tree[k].l == Tree[k].r&&Tree[k].l >= ll&&Tree[k].r <= rr&&Tree[k].val > res)
	{
		return Tree[k].id;
	}
	
	int mid = (Tree[k].l+Tree[k].r)>>1;
	if(ll <= mid&&Tree[k<<1].val > res)  ans = min(ans,query(k<<1,ll,rr,res));
	if(rr >= mid+1&&Tree[k<<1|1].val > res)  ans = min(ans,query(k<<1|1,ll,rr,res));
	return ans;
}

int main()
{
	scanf("%d",&T);
	
	for(int i = 1; i <= T; i++)
	{
		scanf("%d",&a[i]);
	}
	
	build(1,1,T);
	
	for(int i = T; i >= 1; i--)
	{
		ans = T+1;
		int res = query(1,i+1,T,a[i]);
		if(res > T) res = 0;
		B[i-1] = res;
	}
	
	for(int t = 0; t < T; t++) printf("%d ",B[t]);
	printf("\n");
	return 0;
}

B题:
在这里插入图片描述
刚开始给我干懵了,树状数组? 所以我也没做出来,首先,对于每一个数,他们×的最小值应该是1吧,如果每个数都是正数,那么我从第一个数开始删除,最后的结果就是每一个数都乘一个1,但题里可能有负数,我们尽可能让负数乘大值,让剩下的正数从头开始一个一个删除,所以这题应该是贪心算法,先删负数(从后向前删,保持负数的下标最大),最后从前往后删正数,就等效于正数都乘1,负数都乘下标
代码:

//15-B
#include<bits/stdc++.h>
#define ll long long
using namespace std;

int main()
{
	int T;
	scanf("%d",&T);
	
	ll ans = 0;
	ll x;
	for(int i = 1; i <= T; i++)
	{
		scanf("%lld",&x);
		ans += (x > 0)? x:x*i;
	}
	cout<<ans<<endl;
	return 0;
}

C题:
其实到现在我也没完全明白C题,我估计题目表达的意思并不完整,或者就是我太菜了,但从被人的代码里我门能学到什么

大致思路:
因为要求字典序最小,所以我们肯定从1开始,把他赋为0,所有跟1相连的,肯定是2的x次幂(x >= 0&&x <= n-1),因为他只能有一个1,那么我们假设2这个点于1相连,那么与2相连的点(除了1),他的二进制表示里肯定多了一个1,所以这题我们用BFS,层数代表1的格式,1点为0层,和1相连的点为1层,但要满足所有的点都在(0-2^n-1),下一层的二进制就要由上一层二进制合并得到(仅仅我模拟得到的规律,不对就告诉我,对的话告诉我咋推出来的,感谢大佬,好人一生平安)

这里涉及到的二进制的用法可以看我学校的一个大佬写的:二进制
别人的码:

#include<bits/stdc++.h>
using namespace std;
int i,j,n,m,t,ans,x,y,r,f,DP[300005],Q[300005];
vector<int>R[300005];
bool V[300005],T[300005];

struct node 
{
    bool bit[300005];
}S[20];

bool cmp(node a,node b) 
{
    for(int i=1;i<=(1<<n);i++)if(a.bit[i]!=b.bit[i])
        return a.bit[i]>b.bit[i];
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=(1<<n);i++)R[i].clear(),DP[i]=V[i]=T[i]=0;
        while(m--) 
		{
            scanf("%d%d",&i,&j);
            R[i].push_back(j),R[j].push_back(i);
        }
        f=r=DP[1]=0,T[1]=V[1]=1;
        for(i=0;i<R[1].size();i++) //先给第一层赋值
		{
            j=R[1][i];
            DP[j]=(1<<i),T[j]=1,Q[r++]=j;
        }
        while(r!=f)//类BFS
        {
            x=Q[f++],V[x]=1;
            for(i=0;i<R[x].size();i++) 
			{
                j=R[x][i];
                if(V[j])  continue;
                DP[j]|=DP[x];//本层由上一层合并得到
                if(!T[j])T[j]=1,Q[r++]=j;
            }
        }
        
        for(i=1;i<=(1<<n);i++)//i代表第i个数
        {
        	for(j=0;j<n;j++)//j代表第i个数的第j位
            {
            	S[j].bit[i]=(DP[i]>>j)&1;//一位一位取出来,用结构体存储二进制
			}
		}
            
                
        sort(S,S+n,cmp);//按字典序排序,画图看一下就懂了
        
        
        for(i=1;i<=(1<<n);i++) 
		{
            for(ans=j=0;j<n;j++) if(S[j].bit[i]) ans|=(1<<j);//按照结构体对二进制的记录还原
            printf("%d ",ans);
        }
        cout<<endl;
    }
    return 0;
}

D题:
在这里插入图片描述
看到往一堆树里加什么东西就会自然联想到线段树:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+500;
const ll mod=23333;
int n,m,ans = 0;
ll a[N],b[N];
vector<int>G[N];
int L[N],R[N];
struct Tree 
{
	int l,r;
	ll val,Val,tag;
} t[N*4];
int tim=0;

void dfs(int x,int y)//给x设置左右区间
{
	L[x] = ++tim;
	b[tim] = a[x];
	for(int cnt = 0; cnt < G[x].size(); cnt++)
	{
		if(G[x][cnt] != y)
		{
			dfs(G[x][cnt],x);
		}
	}
	R[x] = tim;
}

void update(int p)
{
	t[p].val = (t[p<<1].val+t[p<<1|1].val)%mod;
	t[p].Val = (t[p<<1].Val+t[p<<1|1].Val)%mod;
	return;
}

void build(int p,int l,int r)
{
	t[p].l = l,t[p].r = r;
	if(l == r)
	{
		t[p].val = b[l];
		t[p].Val = (long long)b[l]*b[l]%mod;
		return ;
	}
	int mid = (l+r)>>1;
	build(2*p,l,mid);
	build(2*p+1,mid+1,r);
	update(p);
}

void down(int p)
{
	if(t[p].tag)
	{
		t[p<<1].Val = ((t[p<<1].Val + (t[p<<1].r-t[p<<1].l+1)*t[p].tag*t[p].tag)%mod + 2*t[p<<1].val*t[p].tag%mod)%mod;
		t[p<<1].val = (t[p<<1].val + (t[p<<1].r-t[p<<1].l+1)*t[p].tag)%mod;
		t[p<<1].tag = (t[p<<1].tag + t[p].tag)%mod;
		
		t[p<<1|1].Val = ((t[p<<1|1].Val + (t[p<<1|1].r-t[p<<1|1].l+1)*t[p].tag*t[p].tag)%mod + 2*t[p<<1|1].val*t[p].tag%mod)%mod;
		t[p<<1|1].val = (t[p<<1|1].val + (t[p<<1|1].r-t[p<<1|1].l+1)*t[p].tag)%mod;
		t[p<<1|1].tag = (t[p<<1|1].tag + t[p].tag)%mod;
		
		t[p].tag = 0;
	}
	return;
}

void change(int p,int L,int R,ll x)
{
	if(t[p].l >= L&&t[p].r <= R)
	{
		t[p].Val = ((t[p].Val + (t[p].r-t[p].l+1)*x*x)%mod + 2*t[p].val*x%mod)%mod;
		t[p].val = (t[p].val + (t[p].r-t[p].l+1)*x)%mod;
		t[p].tag = (t[p].tag + x)%mod;
		return;
	}
	
	down(p);
	int mid = (t[p].l+t[p].r)>>1;
	if(L <= mid) change(p<<1,L,R,x);
	if(R >= mid+1) change(p<<1|1,L,R,x);
	update(p);
}

ll ask(int p,int L,int R)
{
	if(t[p].l >= L&&t[p].r <= R)
	{
		return t[p].Val;
	}
	down(p);
	ll ans = 0;
	int mid = (t[p].l+t[p].r)>>1;
	if(L <= mid) ans=(ans+ask(p*2,L,R))%mod;
	if(R >= mid+1) ans=(ans+ask(p*2+1,L,R))%mod;
	return ans;
}

int main()
{
	scanf("%d%d",&n,&m);
	
	for(int i = 1; i <= n; i++)
	{
		scanf("%d",&a[i]);
	}
	
	int u,v;
	for(int i = 1; i <= n-1; i++)
	{
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	
	dfs(1,0);
	build(1,1,n);
	
	int tye,x,y;
	while(m--)
	{
		scanf("%d",&tye);
		if(tye == 1)
		{
			scanf("%d%d",&x,&y);
			change(1,L[x],R[x],(long long)y);
		}
		else if(tye == 2)
		{
			scanf("%d",&x);
			printf("%lld\n",ask(1,L[x],R[x])%mod);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值