2022年5月8号补题


title: 5月8号补题
date: 2022-05-08 10:37:59
author: “胡耀文”
categories: “算法”
tags:

  • “前缀和”
  • “贪心”
  • “状态压缩”
  • “思维”
  • “树状数组”
  • “树形dp”
  • “混合背包”
  • “分组背包”
  • “分层图”
  • “组合数学”

比赛补题

疑难杂题

代码源 + cf + 牛客 + atcoder + 知乎严鸽哥

数字替换 - 题目 - Daimayuan Online Judge

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
struct node{
    int t,x,y;
}q[N];
int mp[N],n;
vector<int>ans;
signed main(){
    for(int i=0;i<N;i++)mp[i]=i;
    cin>>n;
    for(int i=1;i<=n;i++){
        int t,x,y;
        cin>>t;
        if(t==1){
            cin>>x;
            q[i]={t,x};
        }
        else{
            cin>>x>>y;
            q[i]={t,x,y};
        }
    }
    for(int i=n;i>=1;i--){
        int t=q[i].t;
        int x=q[i].x;
        int y=q[i].y;
        if(t==2)mp[x]=mp[y];
        else ans.push_back(mp[x]);
    }
    reverse(ans.begin(),ans.end());
    for(auto x:ans)cout<<x<<" ";
}

画画 - 题目 - Daimayuan Online Judge

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=1100;
int a[N][N];
int get(int i,int j){
    return (i-1)*m+j;
}
struct node{
    int x,y,c;
}q[N*N];
int xx[]={-1,-1,1,0,1,2,2,2,1,0,1,0,1,2};
int yy[]={-1,0,0,-1,1,-1,0,1,0,1,1,2,2,2};
int dx[]={0,1,0,1};
int dy[]={0,0,1,1};
int check(int x,int y){
    int p=-1;
    for(int t=0;t<4;t++){
        int i=x+dx[t];
        int j=y+dy[t];
        if(i<1||i>n||j<1||j>m)return 0;
        int c=a[i][j];
        if(c==-1)continue;
        if(p==-1)p=c;
        if(c!=p)return 0;
    }
    return 1;
}
int vis[N][N];
signed main(){
    ios::sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)cin>>a[i][j];
    }
    int hh=0,tt=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(check(i,j)){
                q[tt++]={i,j,a[i][j]};
                vis[i][j]=1;
                //cout<<"i="<<i<<"  j="<<j<<endl;
            }
        }
    }
    if(hh==tt){
        cout<<"-1";
        return 0;
    }
    stack<node>ans;
    while(hh<tt){
        auto t=q[hh++];
        
        ans.push(t);
        int x=t.x,y=t.y,c=t.c;
        a[x][y]=-1,a[x+1][y]=-1,a[x][y+1]=-1,a[x+1][y+1]=-1;
        for(int t=0;t<14;t++){
            int i=x+xx[t];
            int j=y+yy[t];
            if(i<1||i>n||j<1||j>m)continue;
            if(vis[i][j])continue;
            int c=-1;
            //cout<<"i="<<i<<" j="<<j<<endl;
            if(check(i,j)){
                if(a[i][j]!=-1)c=a[i][j];
                if(a[i+1][j]!=-1)c=a[i+1][j];
                if(a[i][j+1]!=-1)c=a[i][j+1];
                if(a[i+1][j+1]!=-1)c=a[i+1][j+1];
                if(c==-1)c=1;
                q[tt++]={i,j,c};
                vis[i][j]=1;
                //cout<<"i="<<i<<" j="<<j<<endl;
            }
            
        }
    }
    cout<<ans.size()<<'\n';
    while(ans.size()){
        auto t=ans.top();
        ans.pop();
        cout<<t.x<<" "<<t.y<<" "<<t.c<<'\n';
    }
}

小雨坐地铁(分层图_牛客博客 (nowcoder.net)


环的数量 - 题目 - Daimayuan Online Judge

#include<bits/stdc++.h>
using namespace std;
const int N=20,M=1<<N;
int f[M][N],n,m,g[N][N];
long long ans;
int lowbit(int x){return (int)log2(x&-x);}
int cal(int x){
    int cnt=0;
    while(x)cnt+=x&1,x>>=1;
    return cnt;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    int mm=m;
    while(m--){
        int a,b;
        cin>>a>>b;
        a--,b--;
        g[a][b]=g[b][a]=1;
        //f[(1<<a)|(1<<b)][max(a,b)]=1;
    }
    for(int i=0;i<n;i++)f[1<<i][i]=1;
    for(int i=1;i<1<<n;i++){
        int u=lowbit(i);
        for(int j=0;j<n;j++){//j表示我现在走到了点集的哪个点上
            if(!(i>>j&1))continue;
            if(g[u][j])ans+=f[i][j];
            for(int k=u+1;k<n;k++){//k表示我现在要走到哪个点
                if(i>>k&1)continue;
                if(!g[j][k])continue;
                f[i|(1<<k)][k]+=f[i][j];
            }
        }
    }
    cout<<(ans-mm)/2;
}

最大权值划分 - 题目 - Daimayuan Online Judge

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
#define int long long
int a[N],f[N][2],n;
signed main(){
    ios::sync_with_stdio(0);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=2;i<=n;i++){
        if(a[i]<a[i-1]){
            f[i][0]=max(f[i-1][0]+a[i-1]-a[i],f[i-1][1]);
            f[i][1]=max(f[i-1][0],f[i-1][1]);
        }
        else{
            f[i][1]=max(f[i-1][1]+a[i]-a[i-1],f[i-1][0]);
            f[i][0]=max(f[i-1][1],f[i-1][0]);
        }
    }
    cout<<max(f[n][0],f[n][1]);
}

Ayoub’s function - 题目 - Daimayuan Online Judge

组合数学+思维

(8条消息) Ayoub’s function CodeForces - 1301C(组合数学)_starlet_kiss的博客-CSDN博客


括号序列 - 题目 - Daimayuan Online Judge

#include<bits/stdc++.h>
using namespace std;
string s;
const int N=1e6+10;
int f[N],match[N];
signed main(){
    while(cin>>s){
        memset(f,0,sizeof f);
        stack<int>stk;
        int n =s.length();
        for(int i=1;i<=n;i++){
            if(s[i-1]=='(')stk.push(i);
            else{
                if(stk.empty())continue;
                else match[i]=stk.top(),stk.pop();
            }
        }
        for(int i=1;i<=n;i++){
            if(s[i-1]==')'&&match[i])f[i]=f[match[i]-1]+1;
        }
        
        // for(int i=1;i<=n;i++){
        //     if(s[i-1]=='(')stk.push(i);
        //     else{
        //         if(stk.empty())continue;
        //         int t=stk.top();
        //         stk.pop();
        //         match[t]=i;
        //     }
        // }
        // for(int i=n;i>=1;i--){
        //     f[i]=f[i-1];
        //     if(s[i-1]=='('&&match[i]!=0)f[i]=f[match[i]+1]+1;
        // }
        long long ans=0;
        for(int i=1;i<=n;i++){
            if(s[i-1]==')'&&match[i])ans+=f[i];
        }
        cout<<ans<<'\n';
    }
    
}

弗拉德和糖果 II - 题目 - Daimayuan Online Judge

贪心+小思维

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
int sum,ma,n;
signed main(){
    ios::sync_with_stdio(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        sum+=x;
        ma=max(ma,x);
    }
    if(ma-1>sum-ma)puts("NO");
    else puts("YES");
}

任务分配 - 题目 - Daimayuan Online Judge


质区间长度 - 题目 - Daimayuan Online Judge

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int primes[N],cnt,st[N],l,r,k,a[N];
void init(){
    st[1]=1;
    for(int i=2;i<N;i++){
        if(!st[i])primes[cnt++]=i;
        for(int j=0;i*primes[j]<N;j++){
            st[primes[j]*i]=1;
            if(i%primes[j]==0)break;
        }
    }
}
int check(int mid){
    for(int i=l;i<=r;i++){
        int j=i+mid-1;
        if(j>r)break;
        if(a[j]-a[i-1]<k)return 0;
    }
    return 1;
}
signed main(){
    init();
    cin>>l>>r>>k;
    for(int i=l;i<=r;i++){
        if(!st[i])a[i]=a[i-1]+1;
        else a[i]=a[i-1];
        //cout<<i<<" "<<a[i]<<endl;
    }
    if(a[r]-a[l-1]<k){
        //cout<<a[r]<<" "<<a[l-1]<<endl;
        cout<<"-1";
        return 0;
    }
    int L=k,R=r-l+1;
    while(L<R){
        int mid=L+R>>1;
        //cout<<"mid="<<mid<<endl;
        if(check(mid))R=mid;
        else L=mid+1;
    }
    cout<<max(1,R);
}

4009. 收集卡牌 - AcWing题库

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=16,M=1<<N;
int n,k;
double p[N],f[M][N*5+1];
double dfs(int x,int sum,int need){
    if(f[x][sum]>=0)return f[x][sum];
    if(sum>=need*k)return f[x][sum]=0;
    f[x][sum]=0;
    for(int i=0;i<n;i++){
        if(x>>i&1)f[x][sum]+=p[i]*(dfs(x,sum+1,need)+1);
        else f[x][sum]+=p[i]*(dfs(x^(1<<i),sum,need-1)+1);
    }
    return f[x][sum];
}
signed main(){
    cin>>n>>k;
    for(int i=0;i<n;i++)cin>>p[i];
    memset(f,-1,sizeof f);
    cout<<dfs(0,0,n);
}

1212. 地宫取宝 - AcWing题库

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 55, MOD = 1000000007;

int n, m, k;
int w[N][N];
int f[N][N][13][14];

int main()
{
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
        {
            cin >> w[i][j];
            w[i][j] ++ ;
        }

    f[1][1][1][w[1][1]] = 1;
    f[1][1][0][0] = 1;

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
        {
            if (i == 1 && j == 1) continue;
            for (int u = 0; u <= k; u ++ )
                for (int v = 0; v <= 13; v ++ )
                {
                    int &val = f[i][j][u][v];
                    val = (val + f[i - 1][j][u][v]) % MOD;
                    val = (val + f[i][j - 1][u][v]) % MOD;
                    if (u > 0 && v == w[i][j])
                    {
                        for (int c = 0; c < v; c ++ )
                        {
                            val = (val + f[i - 1][j][u - 1][c]) % MOD;
                            val = (val + f[i][j - 1][u - 1][c]) % MOD;
                        }
                    }
                }
        }

    int res = 0;
    for (int i = 0; i <= 13; i ++ ) res = (res + f[n][m][k][i]) % MOD;

    cout << res << endl;

    return 0;
}



4381. 翻转树边 - AcWing题库

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, M = 2 * N;
int h[N], ne[M], e[M], w[M], idx, dp[N];
void add(int a, int b, int c)
{
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int f[N];
void dfs1(int u, int fa)
{
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i], k = w[i];
        if (j == fa)
            continue;
        dfs1(j, u);
        f[u] += f[j] + k;
    }
}
void dfs2(int u, int fa)
{
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i], k = w[i];
        if (j == fa)
            continue;
        dp[j] = dp[u] + (k == 1 ? 0 : 1) - (k == 1 ? 1 : 0);
        dfs2(j, u);
    }
}
int main()
{
    int n;
    cin >> n;
    memset(h, -1, sizeof h);
    for (int i = 1; i < n; i++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b, 0), add(b, a, 1);
    }
    dfs1(1, -1);
    dp[1] = f[1];
    dfs2(1, -1);
    int mx = 0x3f3f3f3f;
    for (int i = 1; i <= n; i++)
        mx = min(mx, dp[i]);
    cout << mx << "\n";
    for (int i = 1; i <= n; i++)
        if (dp[i] == mx)
            cout << i << " ";
    return 0;
}




3111. 回转寿司 - AcWing题库

值域树状数组,离散化后求值

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e5+10;
long long a[N],tr[N],n,L,R,ans,tot;
vector<long long>xs;
void add(int x,int c){
    for(int i=x;i<N;i+=i&-i)tr[i]+=c;
}
long long sum(int x){
    int ans=0;
    for(int i=x;i;i-=i&-i)ans+=tr[i];
    return ans;
}
int get(long long x){
    return lower_bound(xs.begin(),xs.end(),x)-xs.begin()+1;
}
signed main(){
    cin>>n>>L>>R;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        a[i]+=a[i-1];
    }
    for(int i=1;i<=n;i++){
        xs.push_back(a[i]);
        xs.push_back(a[i]-R-1);
        xs.push_back(a[i]-L);
    }
    sort(xs.begin(),xs.end());
    xs.erase(unique(xs.begin(),xs.end()),xs.end());
    tot=xs.size();
    add(get(a[0]),1);
    for(int i=1;i<=n;i++){
        int x=get(a[i]);
        int y=get(a[i]-R-1);
        int z=get(a[i]-L);
        ans+=sum(z)-sum(y);
        add(x,1);
    }
    cout<<ans;
}

4316. 合适数对 - AcWing题库

同方法题


2660. 方伯伯的玉米田 - AcWing题库

利用树状数组的dp

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e4+10,M=550;
int tr[M][N],a[N],n,k,ans;
void add(int k,int v,int c){
    for(int i=k;i<=551;i+=i&-i){
        for(int j=v;j<=5500;j+=j&-j){
            tr[i][j]=max(tr[i][j],c);
        }
    }
}
int sum(int k,int v){
    int ans=0;
    for(int i=k;i;i-=i&-i){
        for(int j=v;j;j-=j&-j){
            ans=max(ans,tr[i][j]);
        }
    }
    return ans;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++){
        for(int j=k;j>=0;j--){
            int t=sum(j+1,a[i]+j)+1;
            ans=max(ans,t);
            add(j+1,a[i]+j,t);
        }
    }
    cout<<ans;
}

AcWing 2978. 最长上升子序列 - AcWing

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
vector<int>a;
int n,tr[N],dp[N];
void add(int x,int c){
    for(int i=x;i<=n;i+=i&-i)tr[i]=max(tr[i],c);
}
int sum(int x){
    int ans=0;
    for(int i=x;i;i-=i&-i)ans=max(ans,tr[i]);
    return ans;
}
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        a.insert(x+a.begin(),i);
    }
    int ans=0;
    for(int i=0;i<n;i++){
        int t=a[i];
        dp[t]=sum(t)+1;
        add(t,dp[t]);
    }
    for(int i=1;i<=n;i++){
        dp[i]=max(dp[i],dp[i-1]);
        cout<<dp[i]<<'\n';
    }
}

作者:我已经不想再做刺客了
链接:https://www.acwing.com/solution/content/105366/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1266. 校门外的树 - AcWing题库

很巧妙的思路
把区间想象成一条条线段
要求区间种数转化为求有多少个线段覆盖过这个区间
r之前的左端点数量减去l之前的右端点数量

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e5+10;
int n,m,l,r,t1[N],t2[N],op;
void add(int x,int tr[]){
    for(int i=x;i<=n;i+=i&-i)tr[i]+=1;
}
int sum(int x,int tr[]){
    int ans=0;
    for(int i=x;i;i-=i&-i)ans+=tr[i];
    return ans;
}
signed main(){
    cin>>n>>m;
    while(m--){
        cin>>op>>l>>r;
        if(op==1){
            add(l,t1);
            add(r,t2);
        }
        else{
            cout<<sum(r,t1)-sum(l-1,t2)<<'\n';
        }
    }
}


2952. 不等式组 - AcWing题库

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N=2e6+10;
int n,t1[N],t2[N],ok,k[N],idx,vis[N],p[N];
void add(int tr[],int x,int c){
    for(int i=x;i<N;i+=i&-i)tr[i]+=c;
}
int sum(int tr[],int x){
    int ans=0;
    for(int i=x;i;i-=i&-i)ans+=tr[i];
    return ans;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        string s;
        cin>>s;
        if(s[0]=='A'){
            int a,b,c;
            cin>>a>>b>>c;
            if(a==0){
                if(b>c){
                    ok++;
                    k[++idx]=1;
                }
                else{
                    k[++idx]=0;
                }
            }
            if(a>0){// k>x
                int x=floor((1.0*c-b)/a);
                if(x<-1e6){
                    ok++;
                    k[++idx]=1;
                }
                else if(x>1e6){
                    k[++idx]=0;
                }
                else{
                    add(t1,x+1e6+1,1);
                    k[++idx]=2;
                    p[idx]=x+1e6+1;
                }
            }
            if(a<0){// k<x
                int x=ceil((1.0*c-b)/a);
                if(x>1e6){
                    ok++;
                    k[++idx]=1;
                }
                else if(x<-1e6){
                    k[++idx]=0;
                }
                else{
                    add(t2,x+1e6+1,1);
                    k[++idx]=3;
                    p[idx]=x+1e6+1;
                }
            }
        }
        if(s[0]=='Q'){
            int x;
            cin>>x;
            x+=1e6+1;
            int ans1=sum(t1,x-1);
            int ans2=sum(t2,N-1)-sum(t2,x);
            cout<<ans1+ans2+ok<<endl;
        }
        if(s[0]=='D'){
            int x;
            cin>>x;
            if(vis[x])continue;
            vis[x]=1;
            if(k[x]==1)ok--;
            if(k[x]==2)add(t1,p[x],-1);
            if(k[x]==3)add(t2,p[x],-1);
        }
    }
}


4081. 选数 - AcWing题库

混合背包dp

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=220,M=200*26;
#define int long long
int f[N][M];
int n,k;
signed main(){
    cin>>n>>k;
    memset(f,-0x3f,sizeof f);
    f[0][0]=0;
    for(int i=1;i<=n;i++){
        int a;
        cin>>a;
        
        int w=0;
        int v=0;
        while(a%5==0)a/=5,w++;
        while(a%2==0)a/=2,v++;
        //cout<<w<<" "<<v<<endl;
        for(int j=k;j>=1;j--){
            for(int t=M-1;t>=w;t--){
                f[j][t]=max(f[j][t],f[j-1][t-w]+v);
            }
        }
    }
    int ans=0;
    for(int i=1;i<M;i++)ans=max(ans,min(i,f[k][i]));
    cout<<ans;
}

3281. 城市规划 - AcWing题库

树形dp 分组背包

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5e4+10,M=2*N;
const long long INF=0x3f3f3f3f3f3f3f3f;
int h[N],ne[M],e[M],idx,n,m,K;
int s[N];
int st[N];
long long w[M];
long long f[N][110];//f【u】【k】表示以u为根选k个点的最大值
long long ans=INF;
void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int fa){
    f[u][0]=0;
    if(st[u])f[u][1]=0,s[u]=1;
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        if(j==fa)continue;
        dfs(j,u);
        s[u]+=s[j];
        for(int k=min(s[u],K);k>=0;k--){
            for(int x=0;x<=min(k,s[j]);x++)
            f[u][k]=min(f[u][k],f[u][k-x]+f[j][x]+w[i]*x*(K-x));
        }
    }
    ans=min(ans,f[u][K]);
}
signed main(){
    memset(h, -1, sizeof h);
    cin>>n>>m>>K;
    while(m--){
        int x;
        scanf("%d",&x);
        st[x]=1;
    }
    for(int i=1;i<n;i++){
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    // for(int i=0;i<N;i++){
    //     for(int j=0;j<110;j++){
    //         f[i][j]=0x3f3f3f3f;
    //     }
    // }
    memset(f,0x3f3f,sizeof f);
    dfs(1,-1);
    cout<<ans;
}

E-禅_牛客小白月赛49 (nowcoder.com)

思维递推

从起点p向两边递推,考虑每个单调区间,这种做法是错误的

比如2 2 2 2 1 10 0

从最左边开始代价是最小的,会一直加

那么既然无法确定起点,如何处理这n方复杂度?

设dp【i】表示从点i开始的代价,考虑答案之间的递推关系

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int n,a[N],T,f[N];
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>T;
    while(T--){
        cin>>n;
        int p;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            if(a[i]==0)p=i;
        }
        if(n==1){
            cout<<"No Solution"<<'\n';
            continue;
        }
        for(int i=1;i<=n;i++)f[i]=0;
        f[p-1]=a[p-1]+1;
        f[p+1]=a[p+1]+1;
        for(int i=p+2;i<=n;i++)f[i]=max(a[i]+1,f[i-1]-a[i]);
        for(int i=p-2;i>=1;i--)f[i]=max(a[i]+1,f[i+1]-a[i]);
        int ans=1e9;
        for(int i=1;i<=n;i++){
            if(i==p)continue;
            ans=min(ans,f[i]);
        }
        cout<<ans<<'\n';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值