第十五届中北大学算法与程序设计竞赛(公开赛)A模拟 C(差分+贪心)D(线段树) E数学 F(线段树) G贪心 H (dp) I (KM算法) K 线段树 L 容斥

题目链接

A-俄罗斯方块

做法:简单模拟

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
int n;
int a[15][15];
int row[15];
int main()
{
    n=read();
    rep(i,1,10) row[i]=10;
    rep(j,1,n)
    {
        int ty=read(),x=read();
        if(ty==1){
            //(i-1,x) (i-1,x+1)
            //(i,x)   (i,x+1)
            per(i,row[x],1){
                if(!a[i][x]&&!a[i][x+1]&&!a[i-1][x]&&!a[i-1][x+1]){
                    a[i][x]=a[i][x+1]=a[i-1][x]=a[i-1][x+1]=1;
                    row[x]=i-2;
                    row[x+1]=i-2;
                    break;
                }
            }
        }
        else if(ty==2){
            //a[i-1][x]
            //a[i][x]  a[i][x+1] a[i][x+2]
            per(i,row[x],1)
            if(!a[i][x]&&!a[i][x+1]&&!a[i][x+2]&&!a[i-1][x]){
                a[i][x]=a[i][x+1]=a[i][x+2]=a[i-1][x]=1;
                row[x]=i-2;
                row[x+1]=i-1;
                row[x+2]=i-1;
                break;
            }
        }
        else if(ty==3){
            //(i,x) (i,x+1) (i,x+2) (i,x+3)
            per(i,row[x],1)
            if(!a[i][x]&&!a[i][x+1]&&!a[i][x+2]&&!a[i][x+3]){
                a[i][x]=a[i][x+1]=a[i][x+2]=a[i][x+3]=1;
                row[x]=i-1;
                row[x+1]=i-1;
                row[x+2]=i-1;
                row[x+3]=i-1;
                break;
            }
        }
        else if(ty==4){
            //    (i-1,x+1)
            //(i,x)(i,x+1)(i,x+2)
            per(i,row[x],1)
            if(!a[i][x]&&!a[i][x+1]&&!a[i][x+2]&&!a[i-1][x+1]){
                //printf("i:%d x:%d\n",i,x);
                a[i][x]=a[i][x+1]=a[i][x+2]=a[i-1][x+1]=1;
                row[x]=i-1;
                row[x+1]=i-2;
                row[x+2]=i-1;
                break;
            }
        }
    }
    //printf("dddd%d\n",a[10][1]);
    rep(i,1,10)
    {
        rep(j,1,10) printf("%d%c",a[i][j],j==10?'\n':' ');
        //puts("");
    }
}
/*
2 1
2 2
1 1
3 3
*/

C-港口

题目言外之意就是使得数组的差分数组为0参考做法来自:牛客

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=1e5+10;
ll a[N],n;


int main()
{

    n=read();
    rep(i,1,n) a[i]=read();
    ll ma1 = 0,ma2 = 0;
    for(int i=1;i<=n;++i)
    {

        if(i == 1) continue;
        ll tmp = a[i]-a[i-1];
        if(tmp > 0) ma1 += tmp;
        else ma2 -= tmp;//减负数其实就是加
        //printf("ma1:%lld ma2:%lld tmp:%lld\n",ma1,ma2,tmp);
    }
    ll ans = max(ma1,ma2);
    printf("%lld\n",ans);

    return 0;
}


/*
7
0 2 2 2 3 3 1 1

ans:2

*/

D-构造数组

做法:离线倒过来模拟,主席树维护一下空的位置即可,代码下方带一个不错的数据

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=5e5+10;
int in[N],a[N],n,ans[N],sum[4*N];

int qu(int id,int l,int r,int k)
{
    if(l==r) return  l;
    int mid=l+r>>1;
    //printf("id:%d su:%d k:%d\n",id,sum[id<<1],k);

    if(k<=sum[id<<1]) return qu(id<<1,l,mid,k);
    return qu(id<<1|1,mid+1,r,k-sum[id<<1]);
}

void up(int id,int l,int r,int pos)
{
    sum[id]--;
    if(l==r)return ;
    int mid=l+r>>1;
    if(pos<=mid) up(id<<1,l,mid,pos);
    else  up(id<<1|1,mid+1,r,pos);

}

void bu(int id,int l,int r)
{

    if(l==r) {
        sum[id]++;
        return ;
    }
    int mid=l+r>>1;
    bu(id<<1,l,mid);
    bu(id<<1|1,mid+1,r);
    sum[id]=sum[id<<1]+sum[id<<1|1];
}
int main()
{
    cin>>n;
    rep(i,1,n) cin>>in[i]>>a[i];

    bu(1,1,n);

    per(i,n,1)
    {
        int id=qu(1,1,n,in[i]);
        //printf("id:%d\n",id);
        ans[id]=a[i];
        up(1,1,n,id);
    }

    rep(i,1,n) printf("%d ",ans[i]);
}
/*
7
1 1
2 2
3 3
2 4
3 5
4 6
5 7
ans:
1 4 5 6 7 2 3
*/

E-简单的线性代数

知识点来自:百度

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=1e3+10;
int a[N][N];
int n;
ll k;
int main()
{
    n=read(),k=read();
    rep(i,1,n) rep(j,1,n) a[i][j]=read();
    rep(i,1,n) rep(j,1,n)
    {
        if(i==j) a[i][j]=1-a[i][j];
        else a[i][j]=-a[i][j];
    }
    rep(i,1,n){
        rep(j,1,n) printf("%d ",a[i][j]);
        puts("");
    }
}

F-集合操作

做法:离线离散化每个x  和 x+1  添加和删除线段树处理就可以了

然后 查询就是 x[i] 到n的最小值即可,维护区间最小值。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
 
inline ll read()
{
    ll x=0,w=1; char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
    while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
    return w==1?x:-x;
}
const int N=1e6+10;
int mi[8*N],len,X[2*N],n,id[N],x[N];
int get(int x)
{
    return lower_bound(X+1,X+1+len,x)-X;
}
void bu(int id,int l,int r)
{
    if(l==r) {
        mi[id]=X[l];
        return ;
    }
    int mid=l+r>>1;
    bu(id<<1,l,mid);
    bu(id<<1|1,mid+1,r);
 
    mi[id]=min(mi[id<<1],mi[id<<1|1]);
    //printf("id:%d mi:%d id<<1:%d id<<1|1:%d\n",id,mi[id],mi[id<<1],mi[id<<1|1]);
 
}
 
void up(int id,int l,int r,int pos,int val)
{
    if(l==r) {
        mi[id]=val;
        return ;
    }
    int mid=l+r>>1;
    if(pos<=mid) up(id<<1,l,mid,pos,val);
    else up(id<<1|1,mid+1,r,pos,val);
    mi[id]=min(mi[id<<1],mi[id<<1|1]);
   // printf("id:%d mi:%d id<<1:%d id<<1|1:%d\n",id,mi[id],mi[id<<1],mi[id<<1|1]);
}
 
int qu(int id,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr) return mi[id];
    int mid=l+r>>1;
    int ans=1e9+10;
    if(ql<=mid) ans=qu(id<<1,l,mid,ql,qr);
    if(qr>mid) ans=min(ans, qu(id<<1|1,mid+1,r,ql,qr));
    return ans;
}
int main()
{
    n=read();
    rep(i,1,n)
    {
        id[i]=read();
        x[i]=read();
        X[++len]=x[i];
        X[++len]=x[i]+1;
    }
    sort(X+1,X+1+len);
    len=unique(X+1,X+1+len)-X-1;
 
    //rep(i,1,n) printf("%d ",X[i]);
    //puts("");
    //printf("len:%d\n",len);
    bu(1,1,len);
    set<int>st;
    rep(i,1,n)
    {
        //printf("i:%d mi:%d\n",i,mi[1]);
        x[i]=get(x[i]);
        //printf("x:%d\n",x[i]);
 
        if(id[i]==1){
            if(st.find(x[i])!=st.end()) continue;
            up(1,1,len,x[i],1e9+10);
            st.insert(x[i]);
        }
        if(id[i]==2){
            if(st.find(x[i])==st.end()) continue;
            //printf("插入x:%d\n",X[x[i]]);
            up(1,1,len,x[i],X[x[i]]);
            st.erase(x[i]);
 
        }
        if(id[i]==3){
 
            //printf("l:%d r:%d\n",x[i],len);
            int ans=qu(1,1,len,x[i],len);
            printf("%d\n",ans);
        }
    }
}

G-数位操作1

做法:刚开始想复杂了,唯一分解去了,其实就是枚举从9到2能否整除即可,注意个位数的n最小应该是1n

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=1e5+10;
int ans[N],len;
int main()
{
    ll n;
    while(~scanf("%lld",&n)){
        if(n<=9){
            printf("1%d\n",n);
            continue;
        }


        len=0;
        int flag=1;
        for(int i=9;i>=2;--i){
            if(n%i==0){
                while(n%i==0) ans[++len]=i,n=n/i;
            }
        }
        if(n!=1){
            if(n>=10) flag=0;
            else ans[++len]=n;
        }
        if(flag==0){puts("-1");continue;}
        //printf("len:%d\n",len);

        sort(ans+1,ans+1+len);
        rep(i,1,len) printf("%d",ans[i]);
        puts("");
    }
}

H-数位操作2

类原题:去年三月份某一场牛客练习赛,知道为啥记得这么清楚,那是我做的第一个dp难题。

做法:n只有50

设dp[i][s][j][k] 为前i位  各位数和为s  后两位是  j k 的方案数,简单推一推就可以了。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int mod=1000009;

const int N=55;
int dp[55][600][10][10],n,s,x;
void add(int &x,int y)
{
    x=(x+y)%mod;
}
int main()
{
    scanf("%d%d%d",&n,&s,&x);
    if(s>=500){
        printf("0");
        return 0;
    }
    for(int i=0;i<=9;++i)
    for(int j=0;j<=9;++j)
        dp[2][i+j][i][j]=1;

    for(int i=3;i<=n;++i){
        rep(ss,0,500)
        rep(j,0,9)
        rep(k,0,9)
        rep(c,0,9)
        {
            if((j*100+k*10+c)%x==0){
                //printf("j:%d\n",j);
                add(dp[i][ss+c][k][c],dp[i-1][ss][j][k]);
            }
        }
    }

    int ans=0;
    rep(i,0,9)
    rep(j,0,9) add(ans,dp[n][s][i][j]);
    printf("%d\n",ans);

}

I-配对

另写了博客:最下面:博客

K-签个到

做法:线段树维护最大值最小值,其实做复杂了,完全维护前后缀的最大值最小值就可以了。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define maxn 1005
#define inf 1e9
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

inline ll read()
{
	ll x=0,w=1; char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
	while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
	return w==1?x:-x;
}
const int N=1e5+10;
ll mx[4*N],a[N],mi[4*N];
int n;
ll m;
void build(int id,int l,int r)
{
    if(l==r){a[l]=read();mi[id]=mx[id]=a[l];return ;}
    
    int mid=l+r>>1;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    mx[id]=max(mx[id<<1],mx[id<<1|1]);
    mi[id]=min(mi[id<<1],mi[id<<1|1]);
}
void up(int id,int l,int r,int pos,ll val)
{
    if(l==r){
        mi[id]=mx[id]=val;
        return ;
    }
    int mid=l+r>>1;
    if(pos<=mid) up(id<<1,l,mid,pos,val);
    else up(id<<1|1,mid+1,r,pos,val);
    mx[id]=max(mx[id<<1],mx[id<<1|1]);
    mi[id]=min(mi[id<<1],mi[id<<1|1]);
}

int main()
{
    n=read(),m=read();
    
    build(1,1,n);
    ll ans=0;

    rep(i,1,n)
    {
        ll t=a[i];
        up(1,1,n,i,t+m*i);
        ans=max(ans,mx[1]-mi[1]);

        up(1,1,n,i,t-m*i);
        ans=max(ans,mx[1]-mi[1]);

        up(1,1,n,i,t);
    }
    printf("%lld\n",ans);
}

L-20200524

假设倍数p=20200524

这类题型有O(plog(p))的公式做法:博客1

也有O(p^2)的同余做法:博客2

但是在这 上面的方法均超时

唯独不会O(p)容斥的做法,今天受教了~

献上代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=20200524;
vector<int> factor;
vector<int> f;
int main(){
    for(int i=1;i*i<=mod;i++){
        if(mod%i==0){
            factor.push_back(i);
            if(i*i!=mod) factor.push_back(mod/i);
        }
    }
    sort(factor.begin(),factor.end());
    int cnt=factor.size();
    f.resize(cnt);
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        for(int i=0;i<cnt;i++) f[i]=n/factor[i];//包含factor[i]的个数 
        for(int i=cnt-2;i>=0;i--){
            for(int j=i+1;j<cnt;j++){
                if(factor[j]%factor[i]==0){
                    f[i]-=f[j];//容斥一下 
                }
            }
        }
        ll ans=0;
        for(int i=0;i<cnt;i++){
            ans+=1LL*f[i]*(m/(mod/factor[i]));//统计答案 最小*facator[i]是mod的整数倍 
        }
        cout<<ans<<endl;
    }
    return 0;
}

持续待补......

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/106319998