2022杭电多校3——补题

Cyber Language

可以用stringstream每次得到一个单词

inline void solve(){
    string s; getline(cin,s);
    stringstream ss; ss<<s;
    string str;
    while(ss>>str){
        char t=str[0];
        t=t-'a'+'A';
        cout<<t;
    }
    cout<<endl;
}

Package Delivery(贪心)

一开始当成区间覆盖做了,发现用一种情况不能被考虑到

按照区间覆盖排完序之后,因为是按照右端点递增排序,所以会有这种情况发生:

[1,4] [5,6] [2,8]

一开始选择了4这个点,但是到第二个区间就会转移到6这个点,第三个区间可以放在4这个点,所以就会导致答案变差

可以将每次取得点存进set中,若选则的点与当前区间为无交集,则插入一个新的点,否则在set中找到第一个大于等于左区间的点拿快递一定更优

注意特判k=1的情况,因为我是先插入了一个点,后面会先++再判断是否等于k(感谢恒哥!)

const int N=1e6+10;
int n,m;
PII g[N];
bool cmp(PII a,PII b){
    if(a.r==b.r) return a.l<b.l;
    return a.r<b.r;
}
inline void solve(){
   cin>>n>>m;
   for(int i=0;i<n;i++){
        int a,b; cin>>a>>b;
        g[i]={a,b};
   }
   if(m==1) cout<<n<<endl;
   else{
       sort(g,g+n,cmp);
       //for(int i=0;i<n;i++) cout<<g[i].l<<' '<<g[i].r<<endl;
       set<int>s;map<int,int>mp;
       s.insert(0);
       int ans=1,pos=g[0].r;
       mp[pos]=1,s.insert(pos);
       for(int i=1;i<n;i++){
           // cout<<g[i].l<<' '<<g[i].r<<' '<<pos<<' '<<cnt<<' '<<ans<<endl;
            if(g[i].l>*(--s.end())){
                pos=g[i].r;
                ans++;
                mp[pos]=1,s.insert(pos);
            }
            else{//g[i].l<=pos
                int now=*s.lower_bound(g[i].l);
                if(now<=g[i].r){
                    mp[now]++;
                    if(mp[now]==m){
                        mp.erase(now);
                        s.erase(now);
                    }
                }
                else{
                    pos=g[i].r;
                    ans++;
                    mp[pos]=1,s.insert(pos);
                }
            }
       }
       cout<<ans<<endl;
   }

Two Permutations(dp)

f[i][j]表示在s中凑到第i位,第i位选P/Q中的方案数(学习自恒哥)

const int N=3e5+10;
int n;
int a[N],b[N],s[2*N];
int posa[N],posb[N];
int f[2*N][3];
inline void solve(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],posa[a[i]]=i;
    for(int i=1;i<=n;i++) cin>>b[i],posb[b[i]]=i;
    for(int i=1;i<=2*n;i++) cin>>s[i];
    if(a[1]==s[1]) f[1][0]=1;
    else f[1][0]=0;
    if(b[1]==s[1]) f[1][1]=1;
    else f[1][1]=0;
    for(int i=2;i<=2*n;i++){
        f[i][1]=f[i][0]=0;
        
        int last=posa[s[i-1]];
        if(last>=1&&last<n&&a[last]==s[i-1]&&a[last+1]==s[i]){
            f[i][0]+=f[i-1][0];
        }
        
        last=i-posb[s[i-1]];
        if(last>=1&&last<=n&&a[last]==s[i]){
            f[i][0]+=f[i-1][1];
        }
        
        last=posb[s[i-1]];
        if(last>=1&&last<n&&b[last]==s[i-1]&&b[last+1]==s[i]){
            f[i][1]+=f[i-1][1];
        }
        
        last=i-posa[s[i-1]];
        if(b[last]==s[i]){
            f[i][1]+=f[i-1][0];
        }
        f[i][1]%=mod;
        f[i][0]%=mod;
    }
    cout<<(f[2*n][1]+f[2*n][0])%mod<<endl;
}

Boss Rush(二分+状压dp+记忆化搜索)

物品数较小,可以用状压枚举出所有情况

找最小时间二分

不能枚举出每种技能的使用先后顺序,所以可以用记忆化搜索求出每种情况下造成的最大伤害

const int N=20,M=1e5+10;
int n,m,mid,l,r;
int f[1<<N];
int s[N][M],t[N],len[N];
int dfs(int u){
    if(f[u]!=-1) return f[u];//剪枝
    int time=0,res=0;
    for(int i=0;i<n;i++){
        if(u>>i&1) time+=t[i+1];
    }
    if(time>mid) return f[u]=0;//mid不能小于总冷却时间
    for(int i=0;i<n;i++){
        if(!(u>>i&1)){
            res=max(res,s[i+1][min(len[i+1],mid-time)]+dfs(u|(1<<i)));//记录最大伤害
        }
    }
    return f[u]=res;
}
bool check(){
    for(int i=0;i<(1<<n);i++) f[i]=-1;
    return dfs(0)>=m;
}
inline void solve(){
    cin>>n>>m;
    l=r=0;
    for(int i=1;i<=n;i++){
        cin>>t[i]>>len[i];
        r+=max(t[i],len[i]);
        for(int j=1;j<=len[i];j++) cin>>s[i][j],s[i][j]+=s[i][j-1];
    }
    int ans=inf;
    while(l<=r){
        mid=l+r>>1;
        if(check()) ans=min(ans,mid),r=mid-1;
        else l=mid+1;
    }
    if(ans==inf) cout<<-1<<endl;
    else cout<<ans-1<<endl;
}

Taxi(二分)

为满足二段性,应先对w进行排序
在这里插入图片描述

我们可以利用上述公式O(1)求出排序后的所有点与(x,y)的最大距离dist

可以二分到达哪个点mid能得到最大的优惠

d i s t [ m i d , n ] > w m i d dist_{[mid,n]}>w_{mid} dist[mid,n]>wmid时,最大优惠是 w m i d w_{mid} wmid,向右二分

否则向左二分

const int N=1e5+10;
int n,m;
int a[N],b[N],c[N],d[N];
struct P{
    int x,y,w;
    bool operator<(const P&t)const{
        return w<t.w;
    }
}p[N];
int ans,x,y;
bool check(int mid){
    int dist=0;
    dist=max(dist,x+y+a[mid]);
    dist=max(dist,x-y+b[mid]);
    dist=max(dist,-x+y+c[mid]);
    dist=max(dist,-x-y+d[mid]);
    ans=max(ans,min(p[mid].w,dist));
    return dist>=p[mid].w;
}
inline void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        int x,y,z; cin>>x>>y>>z;
        p[i]={x,y,z};
    }
    sort(p+1,p+n+1);
    a[n+1]=b[n+1]=c[n+1]=d[n+1]=-inf;
    for(int i=n;i>=1;i--){
        int x=p[i].x,y=p[i].y;
        a[i]=max(a[i+1],-x-y);
        b[i]=max(b[i+1],-x+y);
        c[i]=max(c[i+1],x-y);
        d[i]=max(d[i+1],x+y);
    }
    while(m--){
        cin>>x>>y;
        int l=1,r=n;ans=0;
        while(l<=r){
            int mid=l+r>>1;
            if(check(mid)) l=mid+1;
            else r=mid-1;
        }
        cout<<ans<<endl;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值