hdu多校训练第四场

输出No即可

点击查看代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int mx=3e5+10;

int main(){
    ios::sync_with_stdio(false),cin.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        cout<<"No"<<endl;
    }
    return 0;
}


1006 BIT Subway

做法:按分段函数进行计算即可,一开始用的除法算Wa了,要注意除法可能会出现精度问题,能化简的式子化到最后一起算,中间用除法可能会产生精度问题

点击查看代码
scanf("%d",&t);
    while(t--){
        ans1=0.0,ans2=0.0;
        sum=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%lf",&a[i]);
            sum+=a[i];
            if(ans2>=200) {
                ans2+=a[i]*0.5;
            }else if(ans2>=100){
                ans2+=a[i]*0.8;
            }else ans2+=a[i];
        }
        if(sum>=225){
            ans1=200+(sum-225)*0.5;
        }
        else if(sum<=100){
            ans1=sum;
        }
        else{
            ans1=100+(sum-100)*0.8;
        }
        printf("%.3lf %.3lf\n",ans1,ans2);
    }

1007 Climb Stairs

题意:给定初始血量,每次可以向上跳不超过k格,或者向下一格,血量必须大于等于怪的血量才能成功打怪,打怪后血量增加为该怪的血量,问是否有方案可以打掉所有怪
做法:发现每次向上跳了以后就必须连续打掉后面没打过的怪,因为只能向下走一格,并且每个格子只能访问一次,那么贪心的想,如果跳到某个地方打完以后,能够打完后面的怪,那么就贪心的先打这里,用队列维护即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int maxn=2e5+10;

ll a[maxn];

bool solved(int n,int k){
    deque< pair<ll,int> >q;
    int l=0;
    ll x=a[0];
    for (;l<=n;){
        int st=l;
        l++;
        if(l>n) break;
        if(a[l]<=x){
            x+=a[l];
        }
        else {
            q.push_back({a[l],l});
            ll psum=0;
            while(!q.empty()){
                l++;
                if(l>n) break;
                if(l-st>k) return false;
                if(a[l]<=x){
                    while(!q.empty() && q.back().first<=psum+x+a[l]) psum+=a[q.back().second],q.pop_back();
                    if(q.empty()) {
                        for (int i=st+1;i<=l;i++) x+=a[i];
                        break;
                    }else q.push_back({a[l]+psum,l});
                }else {
                    q.push_back({a[l]+psum,l});
                }
            }
        }
    }
    if(!q.empty()) return false;
    return true;
}

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    int T;
    scanf("%d",&T);
    while(T--){
        int n,k;
        scanf("%d%lld%d",&n,&a[0],&k);
        for (int i=1;i<=n;i++) scanf("%lld",&a[i]);

        int t=solved(n,k);
        if(t) puts("YES");
        else puts("NO");
    }
    return 0;
}

题意:有m种括号,给定长度为n的串,有些地方已经填了对应的左括号,右括号,有些地方是0,表示没有填,问满足括号匹配的序列有多少种
做法:考虑区间dp,设\(dp[l][r]\)表示,\(l-r\)区间内满足条件的方案数,那么转移就是,对于每个l枚举对应的左端点k

\(dp[l][r]+=dp[l+1][k-1]*dp[k+1][r](a[l]>0 ,a[k]==-a[l] 或者 a[k]==0)\)
\(dp[l][r]+=dp[l][r]+dp[l+1][k-1]*dp[k+1][r]*m(a[l]==0,a[k]==0)\)
\(dp[l][r]+=dp[l][r]+dp[l+1][k-1]*dp[k+1][r](a[l]==0,a[k]<0)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int mod=1e9+7;
const int maxn=501;

ll dp[maxn][maxn];
int a[maxn];
int n,m;

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif

    int T;
    cin>>T;
    while(T--){
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++){
            int x;
            scanf("%d",&x);
            a[i]=x;
        }
        for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) dp[i][j]=0;
        for (int i=1;i<=n;i++) dp[i+1][i]=1;
        for (int len=1;len<=n;len++){
            for (int l=1;l<=n-len+1;l++){
                int r=l+len-1;
                for (int k=l+1;k<=r;k++){
                    if(a[l]>0){
                        if(a[k]==0 || a[k]==-a[l]){
                            dp[l][r]=(dp[l][r]+dp[l+1][k-1]*dp[k+1][r]%mod)%mod;
                        }
                    }else if(a[l]==0){
                        if(a[k]==0){
                            dp[l][r]=(dp[l][r]+dp[l+1][k-1]*dp[k+1][r]%mod*m%mod)%mod;
                        }else if(a[k]<0){
                            dp[l][r]=(dp[l][r]+dp[l+1][k-1]*dp[k+1][r]%mod)%mod;
                        }
                    }
                }
            }
        }
        printf("%lld\n",dp[1][n]%mod);
    }
    return 0;
}

1003 Magic

题意:每个魔法药水可以给[i-k+1,i+k-1]区间内增加魔法值,要求每个点的魔法值最少要达到p[i],每个区间[L,R]的魔法药水不能B,问最少使用多少魔法药水可以满足条件

做法:我们令a[i]为前i个魔法塔中加入的魔法原料之和。由于第i个魔法塔的魔法值只与有效半径k内的魔法药水数量有关,则魔法值需求可以表示为
\(a[min(n,i+k-1)]-a[max(0,i-k)]>=p_i\)
对于魔法药水添加的限制可以表示为
\(a[r]-a[l-1]<=B\)
由于每个魔法塔中的原料数量非负,需要满足
\(a[i]-a[i-1]>=0\)
由于要最小解,差分约束建图跑最长路即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;

const int maxn=1e4+10;
const int maxm=1e5+10;

int Head[maxn],Next[maxm],ver[maxm],edge[maxm];
int tot;

void Insert(int u,int v,int w){
    ver[++tot]=v;
    Next[tot]=Head[u];
    Head[u]=tot;
    edge[tot]=w;
}

void init(){
    tot=0;
    memset(Head,-1,sizeof(Head));
    memset(Next,-1,sizeof(Next));
}

int dis[maxn],cnt[maxn],inq[maxn];
const int inf=0x3f3f3f3f;

int spfa(int s,int n){
    for (int i=0;i<=n;i++) dis[i]=-inf,cnt[i]=inq[i]=0;

    inq[s]=1;
    queue<int>q;
    q.push(s);
    dis[s]=0;
    while(!q.empty()){
        int u=q.front();q.pop();
        inq[u]=0;
        for (int i=Head[u];i!=-1;i=Next[i]){
            int v=ver[i];
            if(dis[v]<dis[u]+edge[i]){
                dis[v]=dis[u]+edge[i];
                cnt[v]=cnt[u]+1;

                if(cnt[v]>=n+1) return -1;
                if(!inq[v]){
                    inq[v]=1;
                    q.push(v);
                }
            }
        }
    }

    if(dis[n]<=-inf) return -1;
    return dis[n];
}

void solved(){
    int p,n,k;
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++){       
        scanf("%d",&p);
        Insert(max(0,i-k),min(i+k-1,n),p);
        Insert(i-1,i,0);
    }

    int q;
    scanf("%d",&q);
    for (int i=1;i<=q;i++){
        int l,r,b;
        scanf("%d%d%d",&l,&r,&b);
        Insert(r,l-1,-b);
    }

    printf("%d\n",spfa(0,n));
}

int main(){
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif

    int T;
    cin>>T;
    while(T--){
        init();
        solved();
    }
    return 0;
}

做法:,可以证明,从这个数里任取一些数异或起来的方案,都是可以构造出对应的操作来做到的。
所以,问题完全等价于给n个数,从中选一些数,使得这些数的异或和最大。线性基模板套上即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ll;

const int MAXN=1e5+10;

ll a[MAXN];

int main()
{
    #ifdef lmj_debug
        freopen("1.in","r",stdin);
    #endif
    int T;
    cin>>T;
    while(T--){
        int n,t;
        scanf("%d",&n);t=n;//t是线性基大小
        for(ll i=1;i<=n;++i)scanf("%lld",&a[i]);
        for(ll i=1;i<=n;++i)//异或高斯消元
        {
            for(ll j=i+1;j<=n;++j)
                if(a[j]>a[i])std::swap(a[j],a[i]);
            if(!a[i])
            {
                t=i-1;break;
            }
            for(ll j=51;j>=0;--j)
                if(a[i]&(1ull<<j))
                {
                    for(ll k=1;k<=n;++k)
                        if(i!=k&&(a[k]&(1ull<<j)))a[k]^=a[i];
                    break;
                }
        }
        ll ans=0;
        for(ll i=1;i<=t;++i)
        {
            for(ll j=51;j>=0;--j)//找最高位
                if(a[i]&(1ull<<j))
                {
                    if(!(ans&(1ull<<j)))ans^=a[i];//操作1
                    break;
                }
        }
        printf("%lld\n",ans);
    }
	return 0;
}

题意:一个有向图上边权有两个,求在1-n的路径中,第一边权和最小的前提下,第二边权和最大的方案;
做法:由于dij不能求最长路,因此只能用spfa做,但是spfa必然会T,而题目保证了如果成环也会是零环,那么考虑用dij在第一边权上求出最短路图,然后将0环缩点后变成DAG,再在第二边权上做DAG上的dp即可

posted @ 2022-07-30 10:36  lmj_1  阅读(62)  评论(0)    收藏  举报