首页 > 其他分享 >2022 HDU多校4

2022 HDU多校4

时间:2022-08-27 16:36:30浏览次数:68  
标签:pre HDU le int ll memset 多校 2022 head

Problem

有一个长度为\(n\)的括号序列,括号种类有\(m\)种,现在这个括号序列丢失了一些括号,问可能的合法括号序列个数

  • ()可以匹配当且仅当它们的种类一样
  • \(A\)是合法的,\(x,y\)是某种括号,那么\(xAy\)是合法的当且仅当\(x,y\)匹配
  • \(A、B\)是合法的,则\(AB、BA\)都是合法的

\(1\le n\le 500\),\(1\le m\le 10^9\)

Solve

首先可以想到的是 DP,然后\(n\)只有\(500\),所以可以考虑区间 DP。

\(f_{l,r}\)表示区间\([l,r]\)为合法括号序列

\(g_{l,r}\)表示区间\([l,r]\)为合法括号序列且\(a_l\)和\(a_r\)是匹配的一对括号的方案数

那么转移就是:

  • 如果\(a_l=|a_r|\ne 0\),那么\(f_{l,r}=g_{l,r}=f_{l+1,r-1}\)
  • 如果\(a_l\gt 0,a_r=0\)或者\(a_l=0,a_r\lt 0\),那么\(f_{l,r}=g_{l,r}=f_{l+1,r-1}\)
  • 如果\(a_l=a_r=0\),那么\(f_{l,r}=g_{l,r}=mf_{l+1,r-1}\)

然后再用区间里面的情况去更新\(f_{l,r}\)即可

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
    while(T--){
        int n,m;
        cin>>n>>m;
        vector<int>a(n+1);
        for(int i=1;i<=n;i++) cin>>a[i];
        vector<vector<ll>>g(n+1,vector<ll>(n+1,0)),f(n+1,vector<ll>(n+1,0));
        for(int i=1;i<=n;i++) f[i][i-1]=g[i][i-1]=1;
        for(int len=2;len<=n;len++){
            for(int l=1;l<=n;l++){
                int r=l+len-1;
                if(r>n) break;
                if(a[l]==0 && a[r]==0)
                    f[l][r]=g[l][r]=f[l+1][r-1]*m%mod;
                else if((a[l]>0&&a[r]==0) || (a[l]==0&&a[r]<0) || (a[l]>0&&a[r]<0&&a[l]+a[r]==0))
                    f[l][r]=g[l][r]=f[l+1][r-1];
                else
                    f[l][r]=g[l][r]=0;
                for(int k=l+1;k<r;k++)
                    f[l][r]=(f[l][r]+f[l][k]*g[k+1][r]%mod)%mod;
            }
        }
        cout<<f[1][n]<<'\n';
    }
    return 0;
}

Problem

给定一个\(n\)个点\(m\)条边的有向图,每条边用\((u_i,v_i,e_i,p_i)\)表示从点\(u_i\)到\(v_i\)需要花费\(e_i\)元,并获得\(p_i\)点能量。问从\(1\)号点出发到\(n\),在满足花费最小的前提下,可以得到的最大能量。输出最小花费和能量。

Solve

只考虑花费的话,跑个最短路即可。加入能量后,我们可以考虑先构建一个最短路图,即每个\(v\)可以转移过来的点一个是花费最小的。在这个图上\(dp\)求最大能量即可,但可能不是一个DAG,存在\(0\)环,所以缩点后在DAG上跑DP即可。

存在环的话一定是\(0\)环,因为如果\(x\rightarrow y\rightarrow z\rightarrow x\),那么根据最短路图的性质,有\(d[y]=d[x]+w_1,d[z]=d[y]+w_2,d_[x]=d[y]+w_3\),三个等式左右相加得到\(0=w_1+w_2+w_3\),并且\(w_1,w_2,w_3\ge 0\),故\(w_1=w_2=w_3=0\)

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10,M=3e5+10;
const ll inf=1e18;
struct edges{
    int v,nxt,e,p;
}E[M];
struct nedges{
    int v,nxt,p;
}E_[M],nE[M];
int head[N],cnt,head_[N],cnt_,nhead[N],ncnt;
void add(int u,int v,int e,int p){
    E[cnt]={v,head[u],e,p},head[u]=cnt++;
}
void add_(int u,int v,int p){
    E_[cnt_]={v,head_[u],p},head_[u]=cnt_++;
}
void nadd(int u,int v,int p){
    nE[ncnt]={v,nhead[u],p},nhead[u]=ncnt++;
}
int timing;
int dfn[N],low[N],st[N];
stack<int>s;
int tot;
int id[N];
void tarjan(int u){

     dfn[u]=low[u]=++timing;
     s.push(u),st[u]=1;
     for(int i=head_[u];~i;i=E_[i].nxt){
        int v=E_[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }else if(st[v])  low[u]=min(low[u],dfn[v]);
     }
     if(low[u]==dfn[u]){
            tot++;
            while(s.top()!=u){
                id[s.top()]=tot;
                st[s.top()]=0;
                s.pop();
            }
            id[s.top()]=tot;
            st[s.top()]=0;
            s.pop();
     }
}

ll dist[N],maxp[N];
int vis[N],deg[N];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
    while(T--){
        cnt=cnt_=ncnt=timing=tot=0;
        memset(head,-1,sizeof head);
        memset(head_,-1,sizeof head_);
        memset(nhead,-1,sizeof nhead);
        memset(E,0,sizeof E);
        memset(E_,0,sizeof E_);
        memset(nE,0,sizeof nE);
        memset(dfn,0,sizeof dfn);
        memset(low,0,sizeof low);
        memset(st,0,sizeof st);
        memset(vis,0,sizeof vis);
        memset(id,0,sizeof id);
        memset(deg,0,sizeof deg);
        memset(maxp,0,sizeof maxp);
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++) dist[i]=inf;
        for(int i=1;i<=m;i++){
            int u,v,e,p;
            cin>>u>>v>>e>>p;
            add(u,v,e,p);
        }

        dist[1]=0;
        priority_queue<pair<ll,int>>q;
        q.push({0,1});
        while(q.size()){
           int u=q.top().second;
           q.pop();
           if(vis[u]) continue;
           vis[u]=1;
           for(int i=head[u];~i;i=E[i].nxt){
             int v=E[i].v,e=E[i].e;
             if(dist[v]>dist[u]+e){
                dist[v]=dist[u]+e;
                q.push({-dist[v],v});
             }
           }
        }

        cout<<dist[n]<<" ";
        for(int u=1;u<=n;u++)
            for(int i=head[u];~i;i=E[i].nxt){
                int v=E[i].v,e=E[i].e,p=E[i].p;
                if(dist[v]==dist[u]+e){
                  add_(u,v,p);
                }
            }

        for(int i=1;i<=n;i++)
            if(!dfn[i]) tarjan(i);
        for(int u=1;u<=n;u++)
            for(int i=head_[u];~i;i=E_[i].nxt){
                int v=E_[i].v,p=E_[i].p;
                if(id[u]!=id[v]){
                   nadd(id[u],id[v],p);
                   deg[id[v]]++;
                }
            }
        queue<int>tq;
        for(int i=1;i<=tot;i++)
            if(deg[i]==0) tq.push(i);
        while(tq.size()){
            int u=tq.front();
            tq.pop();
            for(int i=nhead[u];~i;i=nE[i].nxt){
                int v=nE[i].v,p=nE[i].p;
                deg[v]--;
                maxp[v]=max(maxp[v],maxp[u]+p);
                if(!deg[v])tq.push(v);
            }
        }
        cout<<maxp[id[n]]<<'\n';
    }
}

Magic(差分约束、图论)

Problem

有\(n\)个城镇在一条直线上,从\(1\)到\(n\)标号。现在可以在某些城镇放置一个信号发送装置,假设放置这个城镇的位置为\(i\),那么这个城镇可以向\([i-k+1,i+k-1]\)区间内的城镇发送信号。信号发送装置有信号强度,可以自定义,假设为\(power\),那么它可以向范围内的城镇发送强度为\(power\)的信号,并且每个城镇的信号强度可以累加。现在每个城镇\(i\)要求它的信号强度不低于\(p_i\),并且还有\(q\)个限制,每个限制给出一个\((L_i,R_i,B_i)\),要求区间\([L_i,R_i]\)里面放置的信号发送装置的强度和不大于\(B_i\)。问是否可以在满足限制的条件下设置信号发送装置,如果可以,输出最小需要放置的装置的强度和,否则输出\(-1\)

\(1\le n\le 10000\),\(1\le p_i\le 1000\)

Solve

记前\(i\)个城镇的放置信号发送装置的信号强度和为\(s[i]\),那么约束条件可以表示为

  • \(s_{\min(i+k-1,n)}-s_{\max(0,i-k)}\ge p_i\),表示位置\(i\)可以被信号装置覆盖的位置的\(j\)的信号强度和要不小于\(p_i\)
  • \(s_i-s_{i-1}\ge 0\),放置的信号装置强度要大于等于\(0\)
  • \(s_{R_i}-s_{L_i-1}\le B_i\)

这就是差分约束的形式,求最小等价于求最长路,判断无解等价于判断是否存在负环,转换一下等式形式后用SPFA即可

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll inf=1e18;
const int N=1e5+10;
struct edges{
    int v,nxt;
    ll w;
}e[N*5];
int head[N],cnt;
void add(int u,int v,int w){
    e[cnt]={v,head[u],w},head[u]=cnt++;
}
ll p[N],s[N];
ll dist[N];
bool vis[N];
int num[N];
int n;
bool spfa(int s){
    for(int i=0;i<=n;i++) vis[i]=0,num[i]=0;
    for(int i=0;i<=n;i++) dist[i]=-inf;
    queue<int>q;
    q.push(s);
    dist[s]=0;
    while(q.size()){
        int u=q.front();
        q.pop();
        if(num[u]>=n) return false;
        num[u]++;
        vis[u]=0;
        for(int i=head[u];~i;i=e[i].nxt){
            int v=e[i].v;
            ll w=e[i].w;
            if(dist[v]<dist[u]+w){
                dist[v]=dist[u]+w;
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }

        }
    }
    return true;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
    while(T--){
        cnt=0;
        memset(head,-1,sizeof head);
       int k;
       cin>>n>>k;
       for(int i=1;i<=n;i++) cin>>p[i],s[i]=s[i-1]+p[i];
       for(int i=1;i<=n;i++){
        add(i-1,i,0);
        int l=max(1,i-k+1),r=min(n,i+k-1);
        add(l-1,r,p[i]);
       }
       int q;
       cin>>q;
       while(q--){
         int l,r;
         ll b;
         cin>>l>>r>>b;
         add(r,l-1,-b);
       }
       if(!spfa(0))cout<<"-1\n";
       else{
        cout<<dist[n]<<'\n';
       }

    }
}

Climb Stairs(数据结构、模拟)

Problem

有\(n\)层楼,每层楼有一个怪物,血量为\(a_i\),一开始你在第\(0\)层,并拥有\(base\)点攻击力。假设当前在第\(i\)层,每次你可以选择跳到\(i+1,i+2,\cdots,i+k\)中的某一层,即可以向上跳\(j\)层(\(1\le j\le k\)),或者向下跳一层到\(i-1\)层(\(i\ge 1\))。你可以到达一层当且仅当你的攻击力大于这一层怪物的血量,并且这一层没有访问过,每次你到达一层,你可以消灭怪物并将自己的攻击力叠加上怪物血量的数值\(a_i\),问是否可以消灭完所有怪物。

\(1\le n,k\le 10^5\),\(1\le a_i\le 10^9\)

Solve

跳跃方案一定是这种形式\((0,x_1,x_{1}-1,\cdots,1)\),\((x_2,x_2-1,\cdots,x_1+1),\cdots\),我们会从当前位置\(now\)跳到\(x\)后,继续往下跳到第一个之前跳过的地方

假设当前最高的跳过的地方是\(l\),那么从当前位置\(now\)往上跳到一个地方\(x\),这个\(x\)可以往下跳到\(l+1\)需要满足的条件是

\[base\ge a[x]\\ base+a[x]\ge a[x-1]\\ ...\\ base+a[x]+...+a[l+2]\ge a[l+1]\\ \]

记前\(i\)层楼的怪物血量和为\(pre[i]\),转换一下

\[base\ge a[x]=a[x]+pre[x]-pre[x]\\ base\ge a[x-1]-a[x] = a[x-1] - (pre[x] - pre[x-1])=a[x-1]+pre[x-1]-pre[x]\\ base\ge [x-2]-a[x-1]-a[x] =a[x-2] - (pre[x]-pre[x-2])=a[x-2]+pre[x-2]-pre[x]\\ ...\\ base\ge a[l+1]-a[l+2]-...-a[x] =a[l+1] - (pre[x] - pre[l+1])=a[l+1]+pre[l+1]-pre[x]\\ \]

可以发现,对于区间\([l+1,x]\)里的每一项,\(a[i]+pre[i]\)是定值,而\(base\ge max(a[i]+pre[i]) -pre[x]\),就是求一个区间最大值,用数据结构维护一下即可。每次知道这个\(x\),就把区间里的血量累计当攻击力上即可。

Code

#include <bits/stdc++.h>
#define ll long long
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int N=1e5+10;
ll tr[N*4],pre[N];
int a[N];
ll base;
int n,k;
void build(int rt,int l,int r){
    if(l==r){
        tr[rt]=pre[l]+a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    tr[rt]=max(tr[ls],tr[rs]);
}
ll query(int rt,int L,int R,int l,int r){
    if(l<=L&&R<=r){
        return tr[rt];
    }
    ll res=0;
    int mid=(L+R)>>1;
    if(l<=mid) res=max(res,query(ls,L,mid,l,r));
    if(r>mid) res=max(res,query(rs,mid+1,R,l,r));
    return res;
}
bool check(int l,int r){
    ll mx=query(1,1,n,l,r);
    mx=max(0LL,mx-pre[r]);
    return mx<=base;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
   
    while(T--){
        cin>>n>>base>>k;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            pre[i]=pre[i-1]+a[i];
        }
        build(1,1,n);
        bool ok=true;
        int l=1,r=min(k,n);
        while(true){
            int x=-1;
            for(int i=l;i<=r;i++){
                if(check(l,i)){
                    x=i;
                    break;
                }
            }
            if(x==-1){
                ok=false;
                break;
            }
            base+=pre[x]-pre[l-1];
            if(x==n) break;
            r=min(n,l+k),l=x+1;
        }
        if(ok) cout<<"YES\n";
        else cout<<"NO\n";
    }
    return 0;
}

Problem

给定一个长度为\(n\)的序列\(\left\{ a\right\}\),你可以进行这样的操作若干次:选择一个区间\([l,r]\),这个区间的数都变成\(\bigoplus_{i=l}^r a_i\)。问如果可以经过若干次操作使得所有数变成一样的,那么这个最终的数最大是多少。保证序列中至少存在一对数满足\(a_i=a_j\)

Solve

这个问题和找这个序列的最大异或子序列完全等价,线性基的板子题

证明:

首先最后相同的数,一定是原序列中若干数的异或和,其它数可以通过一系列操作变成\(0\)。现在记\(0\)表示最终这个数不贡献答案,\(1\)表示最终这个数贡献答案

  • 若\(00\),表示连续的两个数都不贡献在答案中,记是区间\([i,i+1]\),那么对区间\([i,i+1]\)执行两次操作即可
  • 若\(11\),表示连续的两个数都贡献在答案中,记是区间\([i,i+1]\),假设\(i+2\)个数不贡献在答案中,那么先对\([i,i+1]\)执行一次操作,在对\([i+1,i+2]\)执行两次操作。\(i-1\)不贡献在答案中同理
  • 交错的情况,\(101010\),由于题目保证了存在两个相同的数\(a_x=a_y\)
    • 如果\(a_x\)和\(a_y\)均贡献或均不贡献,他们的贡献都是\(0\),因为异或为\(0\)相当于不贡献。那么就可以根据他们旁边数的贡献性来调整自身的贡献性,就可以构造出\(00\)或\(11\)
    • 如果\(a_x\)和\(a_y\)一个贡献一个不贡献,因为他们是相同的,所以也可以根据旁边的数来调整,只要保证\(a_x,a_y\)的贡献性不同即可,也可以调整出\(00\)或\(11\)

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll p[100];
bool insert(ll x){
    for(int i=60;i>=0;i--){
        if(!(x>>i)) continue;
        if(!p[i]){
            p[i]=x;
            return 1;
        }else x^=p[i];
    }
    return 0;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin>>T;
    while(T--){
        memset(p,0,sizeof p);
        int n;
        cin>>n;
        vector<ll>a(n+1);
        for(int i=1;i<=n;i++) cin>>a[i],insert(a[i]);
        ll ans=0;
        for(int i=60;i>=0;i--)
            if((ans^p[i])>ans) ans^=p[i];
        cout<<ans<<'\n';
    }
}

标签:pre,HDU,le,int,ll,memset,多校,2022,head
From: https://www.cnblogs.com/Arashimu0x7f/p/16630825.html

相关文章

  • springboot+docker发布项目20220827
    1、springboot打包项目 1)、application-dev.yml     对应配置修改 2)、项目package 生成包    3)、生成包         4)、运行......
  • 洛谷 P8496 [NOI2022] 众数 题解
    最近7年最水的D1T1。用权值线段树维护每个数出现的次数,链表维护序列。操作4即合并两棵权值线段树、两个链表,操作2就是删除链表尾的元素并在权值线段树上修改。显......
  • 一切都结束了 - NOI2022 退役记
    大概这篇文章通篇都会用全名。如果有意见可以私信联系我删除部分内容。高一的时候,很喜欢写博客。现在不那么喜欢写博客了,或许是身边有一些人可以分享了。最后一篇博客了......
  • 决定戒烟(20220826)
    以前戒烟过,不过最后又吸了。为什么会复吸?有几个原因:一是觉得吸烟的危害不大。都说吸烟有害健康,但是危害有多大不知道,我自己感觉危害不太大。二是顺其自然,不想为难自己......
  • 2022HDU多校第五场 - 1007 Count Set
    置换群+生成函数+NTT+启发式合并/分治题意给一个1-n的排列p和一个非负整数k,求大小为k的{1,2,3,...n}的子集合T的数量,满足即T的元素按p置换一轮后......
  • git--2022年8月26日
    第一节 git概述 第二节 git安装1、下载地址:https://git-scm.com/downloads2、下载好后傻瓜式安装3、打开gitbash,设置用户签名git......
  • 数据库学习笔记 (本数据库学习笔记以SQL sever 2019 为例进行学习) 20220823 第一节课
    教材及参考数据库课程讲什么?内容安排第一部分数据库原理部分第一章数据库系统概述为什么要学习数据库?数据库的发展改变了人们的工作和生活模式信息积累与运用......
  • 2022-8-27 每日一题-层序遍历+标记+剑指offer-字典树+dfs
    662.二叉树最大宽度难度中等409收藏分享切换为英文接收动态反馈给你一棵二叉树的根节点 root ,返回树的 最大宽度 。树的 最大宽度 是所有层中最大的 宽度......
  • 20220827研讨会2
    NLP  image eventgraph                                   ......
  • 20220827研讨会
    知识图谱动态规划  把起点初始化特殊的值   跑了GNN,得到输出     ----------------------------------------------------------NBFNet ......