牛客算法周周练15题解

A:数列下标

题目描述
给出一个数列 A,求出一个数列B.
其中Bi 表示 数列A中 Ai 右边第一个比 Ai 大的数的下标(从1开始计数),没有找到这一个下标 Bi 就为0
输出数列B

输入描述:
第一行1个数字 n (n ≤ 10000)
第二行n个数字第 i 个数字为 Ai (0 ≤ Ai ≤ 1000000000)
输出描述:
一共一行,第 i 个数和第 i+1 个数中间用空格隔开.

示例1
输入
6
3 2 6 1 1 2
输出
3 3 0 6 6 0

说明
样例不用解释

水题,直接敲吧a…

#include
using namespace std;
const int maxn=(int)1e5+10;
int a[maxn];

int main()
{
    int n,j;
    scanf("%d",&n);
    for(int i=1; i<=n; i++)   scanf("%d",&a[i]);
    bool flag = 1;
    for (int i=1; i<=n; i++)
    {
    // 每输出一位以后有空格放在前面写
        if(flag)    flag=0;
        else    putchar(' '); 
        
        for (j=i+1; j<=n; j++)
        {
            if (a[j]>a[i])
            {
                printf("%d",j);
                break;
            }
        }
        if(j==n+1)   printf("0");
    }
    cout<<endl;
    return  0;
}

B:可持久化动态图上树状数组维护01背包

题目描述
你有一个长度为 n 序列 {a}(序列下标从1开始) ,每次可以从任意位置 i 花费 ai*i 的代价来把 ai 删除。
注意,删除后 ai 后面的数会依次向前补上(下标 -1 ) 。
求把整个序列删完的最小代价。

输入描述:
第一行一个整数 n ,第二行 n 个整数代表该序列。
输出描述:
一行一个整数表示删完序列的最小代价。

示例1
输入
2
3 2
输出
5

备注:
1<=n<=10^6 , |ai|<=10^7
保证答案在 (-2^64, 2^64) 范围内

大wu…整的花里胡哨,直接模拟QAQ(贪心)
删除数的时候,先删除负数,为保证ans尽可能的小,删负数的时候肯定是从后往前删除(不影响前面的负数的下标),我们发现此时负数刚好是ai * i
删完负数,正数从前往后删除即可,这样下标一直是1,代价就是正数本身。

#include 
#define ll long long
using namespace std;

int main()
{
    int n;
    scanf("%d",&n);
    ll ans=0,x;
    for(int i=1; i<=n; i++)
    {
        scanf("%lld",&x);
        ans += x<0 ? x*i : x;
    }
    printf("%lld\n",ans);
}

C:璀璨光滑

牛客算法周周练15题解_第1张图片
牛客算法周周练15题解_第2张图片

#include
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)
        {
            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++)
            for(j=0;j<n;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:树上求和


题目描述
给你一棵根为1的有N个节点的树,以及Q次操作。
每次操作诸如:
1 x y:将节点x所在的子树的所有节点的权值加上y
2 x:询问x所在子树的所有节点的权值的平方和,答案模23333后输出

输入描述:
第一行两个整数N,Q
第二行N个整数,第i个表示节点i的初始权值
接下来N-1行每行两个整数u,v,表示u和v之间存在一条树边
接下来Q行每行一个操作,格式如题目描述
输出描述:
对于每个询问操作,输出一行一个整数,表示答案在模23333后的结果

示例1
输入
5 5
0 0 0 0 0
1 2
1 3
3 4
3 5
1 1 3
1 3 7
1 4 5
1 5 6
2 1
输出
599

备注:
牛客算法周周练15题解_第3张图片

两个模板的结合,额外支持区间乘操作。

#include 
using namespace std;
#define LL long long
#define mod 23333
int cnt, a[100005], b[100005], in[100005], out[100005];
vector<int> G[100005];

typedef struct Tree {
	LL x;
	LL X;
}Tree;
Tree tre[500005], lazy[500005];

void Create(int l, int r, int x)
{
	int m;
	lazy[x].X = 1;
	if(l==r)
	{
		tre[x].x = b[l]%mod;
		tre[x].X = (tre[x].x*tre[x].x)%mod;
		return;
	}
	m = (l+r)/2;
	Create(l, m, x*2);
	Create(m+1, r, x*2+1);
	tre[x].x = (tre[x*2].x+tre[x*2+1].x)%mod;
	tre[x].X = (tre[x*2].X+tre[x*2+1].X)%mod;
}

void GaoX(int l, int r, int x, LL val)
{
	tre[x].x = tre[x].x*val%mod;
	tre[x].X = tre[x].X*val%mod*val%mod;
	if(l!=r)
		lazy[x].X = lazy[x].X*val%mod, lazy[x].x = lazy[x].x*val%mod;
}

void GaoAdd(int l, int r, int x, LL val)
{
	tre[x].X = (tre[x].X+val*val%mod*(r-l+1)%mod+2*val*tre[x].x)%mod;
	tre[x].x = (tre[x].x+val*(r-l+1))%mod;
	if(l!=r)	lazy[x].x = (lazy[x].x+val)%mod;
}

void Update(int l, int r, int x, int a, int b, LL c, int op)
{
	int m;
	if(l>=a && r<=b)
	{
		if(op==4)	GaoAdd(l, r, x, c);
		else	GaoX(l, r, x, c);
		return;
	}
	m = (l+r)/2;
	if(lazy[x].X!=1)
		GaoX(l, m, x*2, lazy[x].X), GaoX(m+1, r, x*2+1, lazy[x].X), lazy[x].X = 1;
	if(lazy[x].x)
		GaoAdd(l, m, x*2, lazy[x].x), GaoAdd(m+1, r, x*2+1, lazy[x].x), lazy[x].x = 0;
	if(a<=m)
		Update(l, m, x*2, a, b, c, op);
	if(b>=m+1)
		Update(m+1, r, x*2+1, a, b, c, op);
	tre[x].x = (tre[x*2].x+tre[x*2+1].x)%mod;
	tre[x].X = (tre[x*2].X+tre[x*2+1].X)%mod;
}

LL Query(int l, int r, int x, int a, int b, int op)
{
	int m;
	LL ans = 0;
	if(l>=a && r<=b) {
		if(op==1)	return tre[x].x;
		else	return tre[x].X;
	}
	m = (l+r)/2;
	if(lazy[x].X!=1)
		GaoX(l, m, x*2, lazy[x].X), GaoX(m+1, r, x*2+1, lazy[x].X), lazy[x].X = 1;
	if(lazy[x].x)
		GaoAdd(l, m, x*2, lazy[x].x), GaoAdd(m+1, r, x*2+1, lazy[x].x), lazy[x].x = 0;
	if(a<=m)
		ans = (ans+Query(l, m, x*2, a, b, op))%mod;
	if(b>=m+1)
		ans = (ans+Query(m+1, r, x*2+1, a, b, op))%mod;
	return ans;
}

void Sech(int u, int p)
{
	int i, v;
	in[u] = ++cnt;
	b[cnt] = a[u];
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(v==p)	continue;
		Sech(v, u);
	}
	out[u] = cnt;
}

int main(void)
{
	int n, T, i, x, y, op;
	scanf("%d%d", &n, &T);
	for(i=1;i<=n;i++)	scanf("%d", &a[i]);
	for(i=1;i<=n-1;i++)
	{
		scanf("%d%d", &x, &y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	Sech(1, 0);
	Create(1, n, 1);
	while(T--)
	{
		scanf("%d", &op);
		if(op==1) {
			scanf("%d%d", &x, &y);
			Update(1, n, 1, in[x], out[x], y, 4);
		}
		else {
			scanf("%d", &x);
			printf("%lld\n", Query(1, n, 1, in[x], out[x], 2));
		}
	}
	return 0;
}

E:算式子

牛客算法周周练15题解_第4张图片
示例1
输入
2 2
1 2
输出
0

说明
牛客算法周周练15题解_第5张图片

#include 
using namespace std;
int n,m,V[2000005]={0},S[2000005]={0};
long long t,ans=0,a[2000005]={0};

int main()
{
    int i,j,l,r;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)  scanf("%d",&j),V[j]++;
    for(i=1;i<=m;i++)
    {
        S[i]=S[i-1]+V[i];
        for(j=i;j<=m;j+=i)  a[j]+=V[i];
    }
    for(i=1; i<=m; i++)
    {
        a[i]+=a[i-1],t=a[i];
        for(j=i; j<=m; j+=i)
        {
            l=j,r=min(j+i-1,m);
            t+=(long long)j/i*(S[r]-S[l-1]);
        }
        ans^=t;
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(题解,模拟,树,算法)