ZJNU 2021-07-14 个人排位赛3 部分题解

完全不会数据结构就比较离谱


A - Roadblock

Problem Link

题意

FJ的农场由\(N\)个点\(M\)条边组成,边存在边权

它每次都会沿着最短路径从点\(1\)走到点\(N\)

现在FJ的牛想选择这张图中的任意一条边,使其边权翻倍

问选择某条边翻倍后,FJ需要多走的距离的最大值是多少(最短路的最大增量)

标签

Dijkstra

SPFA

思路

先在原图中跑一遍SPFA,求出此时的最短路长度\(originalDistance\)并且回溯得到这条路的每一条边

因为只有把最短路上的边翻倍,FJ才有可能走更长的路

所以枚举这条路上的每一条边,翻倍后尝试再跑一遍最短路(这里推荐用Dijkstra),求出此时的最短路长度\(currentDistance\)

答案就是\(currentDistance-originalDistance\)的最大值

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;

int n,m;
vector<P> G[105];
int dis[105];
int pre[105];
vector<P> edge;

int curu,curv;

void spfa()
{
    queue<int> q;
    mst(dis,INF);
    dis[1]=0;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(P pd:G[u])
        {
            int v=pd.first,w=pd.second;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                q.push(v);
                pre[v]=u;
            }
        }
    }
    int pos=n;
    while(pos!=1)
    {
        edge.pb(P(pos,pre[pos]));
        pos=pre[pos];
    }
}

void dijkstra()
{
    priority_queue<P> pq;
    mst(dis,INF);
    dis[1]=0;
    pq.push(P(0,1));
    while(!pq.empty())
    {
        P pp=pq.top();
        pq.pop();
        int u=pp.second;
        if(dis[u]!=-pp.first)
            continue;
        for(P pd:G[u])
        {
            int v=pd.first,w=pd.second;
            if(u==curu&&v==curv||u==curv&&v==curu)
                w<<=1;
            if(dis[u]+w<dis[v])
            {
                dis[v]=dis[u]+w;
                pq.push(P(-dis[v],v));
            }
        }
    }
}

void solve()
{
    cin>>n>>m;
    rep(i,1,m)
    {
        int a,b,c;
        cin>>a>>b>>c;
        G[a].pb(P(b,c));
        G[b].pb(P(a,c));
    }
    spfa();
    int curans=dis[n];
    int maxans=0;
    for(P p:edge)
    {
        curu=p.first;
        curv=p.second;
        dijkstra();
        maxans=max(maxans,dis[n]);
    }
    cout<<maxans-curans<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



B - Grass Planting

Problem Link

题意

FJ有张\(N\)个点\(N-1\)条边的树图

初始时每条边的边权为\(0\)

有两种操作:

\(P\)操作会把\(u\)\(v\)的最短路径上的每条边边权\(+1\)

\(Q\)操作则会询问某条边的边权(保证询问的两个点是相邻的)

标签

Segment Tree

LCA

DFS Clock

思路

暂无,等想好了再改

或者建议直接问余总

代码

//#include<ext/pb_ds/assoc_container.hpp>
//#include<ext/pb_ds/hash_policy.hpp>
#include<bits/stdc++.h>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define all(a) (a).begin(),(a).end()
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
using namespace std;
//using namespace __gnu_pbds;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-12;
const double PI=acos(-1.0);
const ll mod=998244353;
const int dx[8]={0,1,0,-1,1,1,-1,-1},dy[8]={1,0,-1,0,1,-1,1,-1};
mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}

int n;

const int MAXN=100050,MAXF=18;

vector<int> G[MAXN];
int depth[MAXN];
int father[MAXN][MAXF];
bool vis[MAXN];

int lll[MAXN],rrr[MAXN],sz[MAXN];
int clk=0;

void dfs(int pos,int fa)
{
    vis[pos]=true;
    depth[pos]=depth[fa]+1;
    father[pos][0]=fa;
    for(int i=1;i<MAXF;i++)
        father[pos][i]=father[father[pos][i-1]][i-1];
    int cnt=G[pos].size();
    
    lll[pos]=++clk;
    sz[pos]=1;
    
    for(int i=0;i<cnt;i++)
    {
        if(!vis[G[pos][i]])
        {
            dfs(G[pos][i],pos);
            sz[pos]+=sz[G[pos][i]];
        }
    }
    
    rrr[pos]=lll[pos]+sz[pos]-1;
}

int lca(int a,int b)
{
    while(depth[a]<depth[b])
    {
        for(int i=MAXF-1;i>=0;i--)
        {
            if(depth[b]-(1<<i)>=depth[a])
                b=father[b][i];
        }
    }
    while(depth[a]>depth[b])
    {
        for(int i=MAXF-1;i>=0;i--)
        {
            if(depth[a]-(1<<i)>=depth[b])
                a=father[a][i];
        }
    }
    if(a==b)
        return a;
    while(father[a][0]!=father[b][0])
    {
        for(int i=MAXF-1;i>=0;i--)
        {
            if(father[a][i]!=father[b][i])
            {
                a=father[a][i];
                b=father[b][i];
            }
        }
    }
    return father[a][0];
}

struct node
{
    int l,r;
    ll sum,lazy;
}tree[MAXN*4];

void push_up(int id)
{
    tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
}

void push_down(int id)
{
    if(tree[id].lazy)
    {
        int m=tree[id].r-tree[id].l+1;
        tree[id<<1].lazy+=tree[id].lazy;
        tree[id<<1|1].lazy+=tree[id].lazy;
        tree[id<<1].sum+=tree[id].lazy*(m-(m>>1));
        tree[id<<1|1].sum+=tree[id].lazy*(m>>1);
        tree[id].lazy=0;
    }
}

void buildTree(int id,int l,int r)
{
    tree[id].l=l;
    tree[id].r=r;
    tree[id].lazy=0;
    if(l==r)
    {
        tree[id].sum=0;
        return;
    }
    int mid=(l+r)>>1;
    buildTree(id<<1,l,mid);
    buildTree(id<<1|1,mid+1,r);
    push_up(id);
}

void update(int id,int L,int R,ll val)
{
    if(L<=tree[id].l&&R>=tree[id].r)
    {
        tree[id].sum+=val*(tree[id].r-tree[id].l+1);
        tree[id].lazy+=val;
        return;
    }
    push_down(id);
    int mid=(tree[id].r+tree[id].l)>>1;
    if(L<=mid)
        update(id<<1,L,R,val);
    if(R>mid)
        update(id<<1|1,L,R,val);
    push_up(id);
}

ll query_sum(int id,int L,int R)
{
    if(L<=tree[id].l&&R>=tree[id].r)
        return tree[id].sum;
    push_down(id);
    int mid=(tree[id].r+tree[id].l)>>1;
    ll ans=0;
    if(L<=mid)
        ans+=query_sum(id<<1,L,R);
    if(R>mid)
        ans+=query_sum(id<<1|1,L,R);
    return ans;
}

void solve()
{
    int q;
    cin>>n>>q;
    repp(i,1,n)
    {
        int a,b;
        cin>>a>>b;
        G[a].pb(b);
        G[b].pb(a);
    }
    dfs(1,0);
    buildTree(1,1,n);
    while(q--)
    {
        string op;
        cin>>op;
        if(op=="P")
        {
            int u,v;
            cin>>u>>v;
            int z=lca(u,v);
            update(1,lll[u],lll[u],1);
            update(1,lll[v],lll[v],1);
            update(1,lll[z],lll[z],-2);
        }
        else
        {
            int u,v;
            cin>>u>>v;
            if(depth[u]<depth[v])
                swap(u,v);
            cout<<query_sum(1,lll[u],rrr[u])<<'\n';
        }
    }
}
int main()
{
    closeSync;
    //multiCase
    {
        solve();
    }
    return 0;
}



C - Hay Bales

Problem Link

标签

Implementation

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;

int a[10010];

void solve()
{
    int n,s=0;
    cin>>n;
    rep(i,1,n)
    {
        cin>>a[i];
        s+=a[i];
    }
    s/=n;
    int r=0;
    rep(i,1,n)
        r+=max(a[i]-s,0);
    cout<<r<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



D - Cow Photography II

Problem Link

E - Cow Photography I

Problem Link

题意

两道题大致意思相同,直接讲Hard版本

FJ会按某一种顺序让\(N\)头牛排成一队照相

但每次在照相前,总会有一些牛会走出队列,然后随意插入队列中的某些位置

FJ就这样拍了\(5\)张照,每次拍照后都会再让所有牛按原先安排的顺序排回去

现在只知道,对于每一头牛,在这\(5\)张照片中它只会主动走到其他位置一次,剩余四张在拍照时不会主动移动(也可能\(5\)张照片中都不走动)

试根据这\(5\)张照片判断出原先安排的顺序是什么

标签

Sorting

(Hard Version) Hashing

思路

首先抓住题目的要点,也就是\(5\)张照片中每头牛最多主动走到其他位置一次

也就是说,对于任意的两头牛\(a,b\),一开始\(a\)排在\(b\)前面

某一次照相中\(a\)主动走到\(b\)后面

另一次照相中\(b\)主动走到\(a\)前面

这两次照相\(a,b\)的顺序和原先是不同的

但是其他三次照相都是按照原先顺序不会改变

所以可以通过牛的\(id\)反标记其原来的位置,可以利用Hash方法(map/unordered_map/gp_hash_table等)来记录

  • 利用\(pos[i][j]\)表示在第\(i\)次照相中,id为\(j\)的牛的位置

那么在确定最终的顺序时,如果想要判断任意两头牛\(a,b\)的顺序是怎样的,只需要检查在\(5\)次照相中,\(a\)排在\(b\)前面的次数多还是反之

但是由于\(N=20000\),无法根据拓扑排序建图来获得答案

但可以根据快速排序\(O(n\log n)\)的时间复杂度,重写id的判定规则来实现

总时间复杂度为\(O(n\log n\log n)\),使用unordered_map或gp_hash_table时可以优化成常数较大的\(O(n\log n)\)

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;

int n;
int a[5][20010];
unordered_map<int,int> pos[5];

struct node
{
    int id;
    bool operator < (const node& a) const
    {
        int l=0,r=0;
        repp(t,0,5)
        {
            if(pos[t][id]<pos[t][a.id])
                l++;
            else
                r++;
        }
        return l>r;
    }
}ar[20010];

void solve()
{
    cin>>n;
    repp(t,0,5)
    {
        rep(i,1,n)
        {
            cin>>a[t][i];
            pos[t][a[t][i]]=i; //id反记录位置
        }
    }
    rep(i,1,n)
        ar[i].id=a[0][i];
    sort(ar+1,ar+n+1);
    rep(i,1,n)
        cout<<ar[i].id<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



F - Simplifying the Farm

Problem Link

题意

\(N\)个点\(M\)条边的图

要求求出最小生成树的路径权值和,以及不同的最小生成树的种类数

已知同样长度的边最多不超过\(3\)

标签

Minimum Spanning Tree : Kruskal

Counting & Implementation

思路

(是最暴力的分类讨论实现)

最小生成树计数的矩阵树原理不适用于本题中这样的大数据

但题目给出了一条关键限制——同样长度的边最多不超过\(3\)

并且数据范围中每条边的长度限制在\(10^6\)以内

所以可以开一个\(10^6\)大小的 vector<int> eg[i] 来记录长度为\(i\)的边有哪些

根据最小生成树的\(Kruskal\)算法,将边按照从小到大排序后,如果某条边两端的点处于不同的集合内,就将其加入最小生成树中,贡献是将图中独立的集合\(-1\)

可以从\(1\)\(10^6\)枚举长度,忽略两端点已经在同一个集合内的边,将剩余的边取出进行分类讨论


如果相同长度的可取的边有且仅有一条,那么这条边必取

合并这条边的两端点集合

最小生成树总长度\(+i\)\(i\)即当前枚举到的边的长度)

总种类数不变


如果相同长度的可取的边有两条

画图,得以下三种情况(图中每个小圆圈代表独立的集合)

picF-1

对于第一种情况(左一),两条边任意取一条即可

此时最小生成树总长度\(+i\)即可

种类数也就是\(C_2^1=2\),让总种类数乘上\(2\)

对于其余情况(左二、左三),在Kruskal算法中每条边都是需要取的,都会使得图中的独立集合数\(-1\)

此时最小生成树总长度\(+2i\)

总种类数不变


如果相同长度的可取的边有三条

画图,得以下八种情况(蓝色数字表示涉及到的集合数量)

picF-2

按涉及到的集合数量进行讨论:

  • 集合数量为\(2\),仅一种情况(上一),此时三条边任选其一即可
    • 总长度加\(i\)
    • 种类数乘\(C_3^1=3\)
  • 集合数量为\(3\),有两种情况(上二、下四)
    • 对于上二,重复的两条边任选其一,第三条边必选
      • 总长度加\(2i\)
      • 种类数乘\(C_2^1=2\)
    • 对于下四,三条边任选两条便可保证图联通
      • 总长度加\(2i\)
      • 种类数乘\(C_3^2=3\)
  • 集合数量为\(4\),有三种情况(上三、下一、下二)
    • 对于上三,重复的两条边任选其一,第三条边必选
      • 总长度加\(2i\)
      • 种类数乘\(C_2^1=2\)
    • 对于下一和下二,所有边都是必选边
      • 总长度加\(3i\)
      • 种类数不变
  • 集合数量为\(5\)或者\(6\)时,所有边都是必选的
    • 总长度加\(3i\)
    • 种类数不变

最后,根据上述各图的特性,综合\(vector/set\)等容器来判断即可

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;

int n,m;
vector<P> eg[1000050];
int gp[40050];

int fnd(int p)
{
    return p==gp[p]?p:(gp[p]=fnd(gp[p]));
}
void merge(int a,int b)
{
    gp[fnd(b)]=fnd(a);
}
void merge(P pd)
{
    merge(pd.first,pd.second);
}

void kruskal()
{
    int tmp=1;
    ll mincost=0,kinds=1;
    rep(i,1,1000000)
    {
        if(!eg[i].empty())
        {
            vector<P> vec;
            for(P &pd:eg[i])
            {
                int uu=fnd(pd.first),vv=fnd(pd.second);
                if(uu==vv)
                    continue; //集合已经相同的话不纳入选择范围
                if(uu>vv)
                    swap(uu,vv); //小在前大在后,方便判断
                vec.pb(P(uu,vv));
            }
            sort(vec.begin(),vec.end());
            if(vec.size()==1) //相同长度的(可选)边只有一条时
            {
                mincost+=i;
                merge(vec[0]);
            }
            else if(vec.size()==2) //相同长度的(可选)边有两条时
            {
                set<int> st; //涉及到的集合数量
                for(P pd:vec)
                {
                    st.insert(pd.first);
                    st.insert(pd.second);
                }
                if(st.size()==2)
                {
                    mincost+=i;
                    kinds=(kinds*2)%mod;
                    merge(vec[0]);
                }
                else
                {
                    mincost+=i*2;
                    merge(vec[0]);
                    merge(vec[1]);
                }
            }
            else if(vec.size()==3) //相同长度的(可选)边有三条时
            {
                set<int> st;
                for(P pd:vec)
                {
                    st.insert(pd.first);
                    st.insert(pd.second);
                }
                if(st.size()==2)
                {
                    mincost+=i;
                    kinds=(kinds*3)%mod;
                    merge(vec[0]);
                }
                else if(st.size()==3)
                {
                    if(vec[0]==vec[1]||vec[1]==vec[2]) //存在两条边是相同的
                    {
                        if(vec[0]==vec[1])
                        {
                            mincost+=i*2;
                            kinds=(kinds*2)%mod;
                            merge(vec[0]);
                            merge(vec[2]);
                        }
                        else
                        {
                            mincost+=i*2;
                            kinds=(kinds*2)%mod;
                            merge(vec[0]);
                            merge(vec[1]);
                        }
                    }
                    else
                    {
                        mincost+=i*2;
                        kinds=(kinds*3)%mod;
                        merge(vec[0]);
                        merge(vec[1]);
                    }
                }
                else if(st.size()==4)
                {
                    if(vec[0]==vec[1]||vec[1]==vec[2])
                    {
                        if(vec[0]==vec[1])
                        {
                            mincost+=i*2;
                            kinds=(kinds*2)%mod;
                            merge(vec[0]);
                            merge(vec[2]);
                        }
                        else
                        {
                            mincost+=i*2;
                            kinds=(kinds*2)%mod;
                            merge(vec[0]);
                            merge(vec[1]);
                        }
                    }
                    else
                    {
                        mincost+=i*3;
                        merge(vec[0]);
                        merge(vec[1]);
                        merge(vec[2]);
                    }
                }
                else if(st.size()>=5)
                {
                    mincost+=i*3;
                    merge(vec[0]);
                    merge(vec[1]);
                    merge(vec[2]);
                }
            }
        }
    }
    //mincost%=mod;
    cout<<mincost<<' '<<kinds<<'\n';
}

void solve()
{
    cin>>n>>m;
    if(n==1)
    {
        cout<<"0 1\n";
        return;
    }
    rep(i,1,m)
    {
        int u,v,w;
        cin>>u>>v>>w;
        eg[w].pb(P(u,v));
    }
    rep(i,1,n)
        gp[i]=i;
    
    kruskal();
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



G - Umbrellas for Cows

Problem Link

题意

\(N\)头牛分散在某一长度为\(M\)的数轴上的某些点

已知买一把宽度为\(i\)的雨伞需要花费\(C_i\)

如果要将位置为\(a,b\)的两头牛用一把雨伞盖住,则雨伞的宽度至少要为\(b-a+1\)

问将所有牛盖住的最小花费是多少

在最优方案中,雨伞是允许重叠的

标签

Dynamic Programming

思路

明显的\(O(n^2)\)\(DP\)

因为雨伞并不是说随着宽度\(i\)增加,\(C_i\)也一定会增加的,并且题目允许雨伞重叠

所以可以将宽度较小的雨伞与比其宽度大的雨伞中的最小花费取小,使得\(C_i\)最后呈一个递增的顺序

于是便可以进行\(DP\)转移,转移方程为

\[distance=p[i]-p[j+1]+1\\ dp[i]=min(dp[i],dp[j]+c[distance]),\ j\lt i \]

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;

int n,m;
int p[5050];
ll c[100050];
ll dp[5050];

void solve()
{
    cin>>n>>m;
    rep(i,1,n)
        cin>>p[i];
    rep(i,1,m)
        cin>>c[i];
    sort(p+1,p+n+1);
    per(i,m-1,1)
        c[i]=min(c[i],c[i+1]);
    //rep(i,1,m) cerr<<"? "<<i<<' '<<c[i]<<'\n';
    dp[0]=0;
    rep(i,1,n)
    {
        dp[i]=LINF;
        repp(j,0,i)
            dp[i]=min(dp[i],dp[j]+c[p[i]-p[j+1]+1]);
    }
    cout<<dp[n]<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



H - Routing Schemes

Problem Link

防AK 😦




I - SegmentTree!

Problem Link

标签

Segment Tree

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;

const int MAXN=1e5+50;

ll a[MAXN];

struct node
{
	int l,r;
	ll lazy,maxn;
}tree[MAXN<<2];

void push_up(int id)
{
	tree[id].maxn=max(tree[id<<1].maxn,tree[id<<1|1].maxn);
}

void push_down(int id)
{
	if(tree[id].lazy)
	{
		int m=tree[id].r-tree[id].l+1;
		tree[id<<1].lazy+=tree[id].lazy;
		tree[id<<1|1].lazy+=tree[id].lazy;
		tree[id<<1].maxn+=tree[id].lazy;
		tree[id<<1|1].maxn+=tree[id].lazy;
		tree[id].lazy=0;
	}
}

void buildTree(int id,int l,int r)
{
	tree[id].l=l;
	tree[id].r=r;
	tree[id].lazy=0;
	if(l==r)
	{
        tree[id].maxn=a[l];
		return;
	}
	int mid=(l+r)>>1;
	buildTree(id<<1,l,mid);
	buildTree(id<<1|1,mid+1,r);
	push_up(id);
}

void update(int id,int L,int R,ll val)
{
	if(L<=tree[id].l&&R>=tree[id].r)
	{
		tree[id].maxn+=val;
		tree[id].lazy+=val;
		return;
	}
	push_down(id);
	int mid=(tree[id].r+tree[id].l)>>1;
	if(L<=mid)
		update(id<<1,L,R,val);
	if(R>mid)
		update(id<<1|1,L,R,val);
	push_up(id);
}

ll query_max(int id,int L,int R)
{
	if(L<=tree[id].l&&R>=tree[id].r)
		return tree[id].maxn;
	push_down(id);
	int mid=(tree[id].r+tree[id].l)>>1;
	ll ans=-LINF;
	if(L<=mid)
		ans=max(ans,query_max(id<<1,L,R));
	if(R>mid)
		ans=max(ans,query_max(id<<1|1,L,R));
	return ans;
}

int n,m;

void solve()
{
    cin>>n>>m;
    rep(i,1,n)
        cin>>a[i];
    buildTree(1,1,n);
    while(m--)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int p; ll v;
            cin>>p>>v;
            update(1,p,p,v-a[p]);
            a[p]=v;
        }
        else
        {
            int l,r;
            cin>>l>>r;
            cout<<query_max(1,l,r)<<'\n';
        }
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



J - Escaping the Farm

Problem Link

题意

\(N\)个数,要求选择一些数,使得按照十进制相加时不会产生进位

问最多能选择的数的数量

标签

Brute Force

思路

由于\(N\)最大只有\(20\),直接\(O(2^{20})\)枚举出所有状态,模拟加法判断进位即可

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mst(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;

int n,a[22][11];

bool check(int stat)
{
    rep(j,0,9)
    {
        int t=0;
        rep(i,1,n)
        {
            if(stat>>(i-1)&1)
                t+=a[i][j];
        }
        if(t>9)
            return false;
    }
    return true;
}

void solve()
{
    cin>>n;
    rep(i,1,n)
    {
        int d;
        cin>>d;
        int p=0;
        while(d)
        {
            a[i][p]=d%10;
            d/=10;
            p++;
        }
    }
    int r=0;
    repp(t,1,1<<n)
    {
        if(check(t))
            r=max(r,(int)__builtin_popcount(t));
    }
    cout<<r<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}



K - Counting Graphs

Problem Link

防AK 😦




https://blog.csdn.net/qq_36394234/article/details/118733180

posted @ 2021-07-14 17:34  StelaYuri  阅读(100)  评论(0编辑  收藏  举报