【牛客15周算法周赛】 ABD题解

10 篇文章 0 订阅
1 篇文章 0 订阅

A:数列下标

题意:找出每个位置右边第一个比自身大的位置

思路:单调栈模板题。然而这个题目数据水,竟然暴力都能过

AC代码:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e4+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'|ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll n;
ll r[maxn];
stack<ll> s;
ll a[maxn];

void solve()
{
    while(!s.empty()) s.pop();
    per(i,n,1)
    {
        while(s.size()&&a[s.top()]<= a[i]) s.pop();
        if(s.empty()) r[i] = 0;
        else r[i] = s.top();
        s.push(i);
    }
    rep(i,1,n)
    printf("%lld%c",r[i],i==n?'\n':' ');
}

int main()
{
        cin>>n;
        rep(i,1,n) a[i] = read();
        solve();
        return 0;
}


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

题意:每个位置取出的代价是a[i]*i,问最小花费

思路:挂羊头卖狗肉,别理这个题目可能95%的人都能过。如果全是正数,我们就直接每次挑开头就行了(毕竟每个a[i]都要选,谁不想乘个小的i呢)。如果有负数,那就是能对花费减少的,我希望i越大越好,所以我就从后往前挑负数,因为这样不会影响到前面的负数要乘的i。所以这个题就贪心一下就行了。

AC代码:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e6+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'|ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll a[maxn];
ll b[maxn];

int main()
{
    ll n; cin>>n;
    rep(i,1,n)
    a[i] = read();
    ll ans = 0;
    per(i,n,1)
    {
        if(a[i]<=0) ans += a[i]*i;
    }
    rep(i,1,n) if(a[i]>0) ans += a[i];
    cout<<ans<<'\n';
    return 0;
}

D:树上求和

题意:给一颗数,修改:对子树+y。查询:子树的平方和

思路:先处理一遍dfs序,把这颗树转化成线性的区间来表示。然后用线段树来解决。线段树一个单元多加两个主要的变量,a[i].sum表示区间和,a[i].po表示区间平方和。观察规律发现,其实对【L,R】+ y,对每个元素x其实就增加了2xy + y2,那么总体增加的就是2ysum(l,r) + (r-l+1)y2。后面就是(bug树)线段树的常规操作了

AC代码:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 2e6+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 23333;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'|ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

vector<vector<ll> > D(maxn);
int n;
ll c[maxn];
ll vis[maxn];
ll L[maxn];
ll R[maxn];
ll idx = 1;
ll arr[maxn];

ll d[maxn];
ll sum1[maxn];
ll sum2[maxn];

typedef struct Tree
{
    ll l;
    ll r;
    ll sum;
    ll po;
    ll lazy;
} T;
T a[maxn<<2];

void update(ll k)
{
    a[k].sum = (a[k<<1].sum + a[k<<1|1].sum)%mod;
    a[k].po = (a[k<<1].po + a[k<<1|1].po)%mod;
}

void build_tree(ll k, ll l, ll r)
{
    a[k].l = l;
    a[k].r = r;
    a[k].lazy = 0;
    if(l==r)
    {
        a[k].sum = 0;
        a[k].po = 0;
        return ;
    }
    ll mid = (l+r)>>1;
    build_tree(k<<1,l,mid);
    build_tree(k<<1|1,mid+1,r);
    update(k);
}

void pushdown(ll k)
{
    if(a[k].l==a[k].r)
    {
        a[k].lazy=0;
        return;
    }
    ll y = a[k].lazy ;
    a[k<<1].po  =  ( a[k<<1].po + a[k<<1].sum * 2 * y + (a[k<<1].r - a[k<<1].l + 1)* y*y ) % mod;
    a[k<<1].sum = (a[k<<1].sum+y * (a[k<<1].r - a[k<<1].l + 1) ) %mod;

    y = a[k].lazy ;
    a[k<<1|1].po =   (a[k<<1|1].po + a[k<<1|1].sum * 2 * y + (a[k<<1|1].r - a[k<<1|1].l + 1)*y*y ) % mod;
    a[k<<1|1].sum  = (a[k<<1|1].sum + y * (a[k<<1|1].r - a[k<<1|1].l + 1) )  % mod;

    a[k<<1].lazy += a[k].lazy;
    a[k<<1|1].lazy += a[k].lazy;
    a[k].lazy = 0;
}

void change_for_range(ll k, ll l, ll r,ll val)
{
    if(a[k].lazy)   pushdown(k);
    if(a[k].l>r||a[k].r<l)  return ;
    else if(a[k].l>=l&&a[k].r<=r)
    {
        a[k].lazy += val; 
        a[k].po = ( a[k].po + a[k].sum * 2 * val + val*val * (a[k].r - a[k].l + 1) ) % mod;

        a[k].sum =( a[k].sum + val*(a[k].r - a[k].l +1) ) % mod;
        return ;
    }
    ll mid = (l+r)>>1;
    change_for_range(k<<1,l,r,val);
    change_for_range(k<<1|1,l,r,val);
    update(k);
}


ll Query_for_range(ll k,ll l, ll r)
{
    if(a[k].lazy)  pushdown(k);
    if(a[k].l>r||a[k].r<l)  return 0;
    else if(a[k].l>=l&&a[k].r<=r)
    {
        return a[k].po;
    }

    return (Query_for_range(k<<1, l, r) + Query_for_range(k<<1|1,l,r) ) % mod;
}


void dfs_for_tree(int x)
{
        L[x] = ++idx; //idx初始化为0
        for(int i=0;i<D[x].size();i++)
        {
                vis[x] = 1;
                if(!vis[D[x][i]])
                dfs_for_tree(D[x][i]);
        }
        R[x] = idx;
}

void init()
{
    idx = 0;
    for(int i=1;i<=n;i++)
    vis[i]=0, D[i].clear(),c[i]=0;
}

int main()
{
    while(~scanf("%d",&n))
    {
            init();
            build_tree(1,1,n);
            int m;
            scanf("%d",&m);
            rep(i,1,n) arr[i] = read();
            for(int i=1;i<=n-1;i++)
            {
                ll x = read(), y = read();
                D[x].push_back(y);
                D[y].push_back(x);
            }
            dfs_for_tree(1);
            for(int i=1;i<=n;i++)  change_for_range(1,L[i],L[i],arr[i]);

            for(int i=1;i<=m;i++)
            {
                 ll flag = read();
                 if(flag==1)
                 {
                     ll x = read(), y = read();
                     ll l = L[x];
                     ll r = R[x];
                     change_for_range(1,l,r,y);
                 }
                 else
                 {
                     ll x = read();
                     ll l = L[x];
                     ll r = R[x];
                     ll res = (Query_for_range(1,l,r))%mod;
                     printf("%lld\n",res);
                 }
            }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值