noip模拟61[恢复]

noip模拟61 solutions

怎么说考场上还是有一点点小小的思路,不过总是想叉了

该切掉的切不掉而且最近挂分非常严重,不是看不清题就是忘记输出

T1 交通

没想到吧可以直接并查集。。。。。

一开始以为是组合数,后来以为是容斥,再后来就不想写了

因为确定一条边之后就会一连串的确定好多边

具体来说就是对边进行连边,在同一个环里的,会唯一确定,也就是说每一个环只有两种方案

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1000005;
const int mod=998244353;
int n,ans;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
bool vis[N];
int rd[N][2],cd[N][2];
int fa[N*2];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
signed main(){
    #ifdef oj 
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
    #endif
    scanf("%lld",&n);
    fo(i,1,2*n){
        fa[i]=i;
        int x,y;scanf("%lld%lld",&x,&y);
        if(cd[x][0])cd[x][1]=i;
        else cd[x][0]=i;
        if(rd[y][0])rd[y][1]=i;
        else rd[y][0]=i;
    }
    fo(i,1,n){
        int fx=find(cd[i][0]),fy=find(cd[i][1]);
        if(fx!=fy)fa[fx]=fy;
        fx=find(rd[i][0]);fy=find(rd[i][1]);
        if(fx!=fy)fa[fx]=fy;
    }
    int bas=0;
    fo(i,1,2*n){
        if(!vis[find(i)])bas++,vis[find(i)]=true;
    }
    printf("%lld",ksm(2ll,bas));
}

T2 小P的单调数列

\(\mathcal{O(n^3)}\)的DP还是非常好想的,但是我那个定义无法用线段树优化

所以这个正解就是贪心!!!没想到吧啊

对于求平均数的这类题,当然是段数越小越好

但是由于第一段只能是上升的,所以最优策略就是一段上升或者一段上升+一段下降

直接维护最长上升序列就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e5+5;
int n,a[N],lsh[N],lh,rak[N];
double ans;
struct node{
    #define ls x<<1
    #define rs x<<1|1
    int sum[N*4];
    void pushup(int x){sum[x]=max(sum[ls],sum[rs]);}
    void ins(int x,int l,int r,int pos,int v){
        if(l==r)return sum[x]=max(sum[x],v),void();
        int mid=l+r>>1;
        if(pos<=mid)ins(ls,l,mid,pos,v);
        else ins(rs,mid+1,r,pos,v);
        pushup(x);
    }
    int query(int x,int l,int r,int ql,int qr){
        if(ql>qr)return 0;
        if(ql<=l&&r<=qr)return sum[x];
        int mid=l+r>>1,ret=0;
        if(ql<=mid)ret=max(ret,query(ls,l,mid,ql,qr));
        if(qr>mid)ret=max(ret,query(rs,mid+1,r,ql,qr));
        return ret;
    }
    #undef ls
    #undef rs
}xds;
int f[N],g[N];
signed main(){
    #ifdef oj
        freopen("b.in","r",stdin);
        freopen("b.out","w",stdout);
    #endif
    scanf("%lld",&n);
    fo(i,1,n)scanf("%lld",&a[i]),lsh[++lh]=a[i];
    sort(lsh+1,lsh+n+1);
    lh=unique(lsh+1,lsh+n+1)-lsh-1;
    fo(i,1,n){
        rak[i]=lower_bound(lsh+1,lsh+n+1,a[i])-lsh;
        int mx=xds.query(1,1,lh,1,rak[i]-1);
        f[i]=mx+a[i];
        xds.ins(1,1,lh,rak[i],f[i]);
    }
    memset(xds.sum,0,sizeof(xds.sum));
    fu(i,n,1){
        int mx=xds.query(1,1,lh,1,rak[i]-1);
        g[i]=mx+a[i];
        xds.ins(1,1,lh,rak[i],g[i]);
    }
    fu(i,n,1)g[i]=max(g[i],g[i+1]);
    fo(i,1,n){
        //cout<<f[i]<<" "<<g[i]<<endl;
        ans=max(ans,1.0*f[i]);
        ans=max(ans,1.0*(f[i]+g[i+1])/2.0);
    }
    printf("%.3lf",ans+1e-12);
}

T3 矩阵

这个题是我第一次在考试中遇到构造题

我考场上只会只有行和列的,并且还没有输出步数

首先,我们知道这个对角线是用来干啥的,用来平衡你那些不相等的行和列

所以我们直接对前两行操作,先操作第一行,让主对角线的前两个数相等

然后一个一个主对角线的操作,一直到最后一个就可以直接消掉了

列是一样的,因为此时你已经把每一行每一列每一个对角线都操作过了,如果没有成功,直接-1

要么就是直接输出

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1005;
const int inf=0x3f3f3f3f;
int n,m,a[N][N];
int cz[N*6][3],cnt;
signed main(){
    #ifdef oj
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
    #endif
    scanf("%lld%lld",&n,&m);
    fo(i,1,n)fo(j,1,m)scanf("%lld",&a[i][j]);
    cz[++cnt][0]=1;cz[cnt][1]=1;cz[cnt][2]=a[2][2]-a[1][1];
    fo(i,1,m)a[1][i]+=cz[cnt][2];
    cz[++cnt][0]=3;cz[cnt][1]=0;cz[cnt][2]=-a[1][1];
    fo(i,1,min(n,m))a[i][i+0]+=cz[cnt][2];
    fo(i,2,m-1){
        cz[++cnt][0]=3;cz[cnt][1]=i-1;cz[cnt][2]=a[2][i]-a[1][i];
        fo(j,1,min(n,m-cz[cnt][1]))a[j][j+cz[cnt][1]]+=cz[cnt][2];
    }
    cz[++cnt][0]=3;cz[cnt][1]=m-1;cz[cnt][2]=a[2][m]-a[1][m];a[1][m]=a[2][m];
    fo(i,1,m)if(a[1][i]){
        cz[++cnt][0]=2;cz[cnt][1]=i;cz[cnt][2]=-a[1][i];
        fo(j,1,n)a[j][i]+=cz[cnt][2];
    }
    fo(i,2,n-1){
        cz[++cnt][0]=3;cz[cnt][1]=1-i;cz[cnt][2]=a[i][2]-a[i][1];
        fo(j,i,min(n,m-cz[cnt][1]))a[j][j+cz[cnt][1]]+=cz[cnt][2];
    }
    cz[++cnt][0]=3;cz[cnt][1]=1-n;cz[cnt][2]=a[n][2]-a[n][1];a[n][1]=a[n][2];
    fo(i,1,n)if(a[i][1]){
        cz[++cnt][0]=1;cz[cnt][1]=i;cz[cnt][2]=-a[i][1];
        fo(j,1,m)a[i][j]+=cz[cnt][2];
    }
    bool flag=true;
    fo(i,1,n)fo(j,1,m)if(a[i][j])flag=false;
    if(flag){
        printf("%lld\n",cnt);
        fo(i,1,cnt)printf("%lld %lld %lld\n",cz[i][0],cz[i][1],cz[i][2]);
    }
    else printf("-1");
}

T4 花瓶

其实我在考场上就想到斜率优化了

但是因为前缀和无序就放弃了,还白白浪费我1h

无序就直接排序啊,然后你就有了\(\mathcal{O(n^2logn)}\)的做法

然后你发现我可以用前面更新后面,然后就可以直接扫单调队列了,

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=5005;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,a[N],pre[N];
int f[N][N],ans=-inf;
int sta[N][N],bot[N],top[N];
struct node{
    int va,id;
    bool operator < (node a)const{
        return va>a.va;
    }
}ji[N];
int rak[N],sum[N],rk[N],wo[N];
double get(int i,int x,int y){
    //if(pre[x]==pre[y])return 
    return 1.0*(f[i][x]-f[i][y])/(1.0*pre[x]-pre[y]);
}
signed main(){
    #ifdef oj
        freopen("d.in","r",stdin);
        freopen("d.out","w",stdout);
    #endif
    scanf("%lld",&n);
    fo(i,1,n)scanf("%lld",&a[i]),pre[i]=pre[i-1]+a[i];
    fo(i,0,n)ji[i].va=pre[i],ji[i].id=i;
    sort(ji+0,ji+n+1);
    fo(i,0,n)rak[ji[i].id]=i;
    //fo(i,1,n)cout<<pre[i]<<" ";cout<<endl;
    memset(f,-0x3f,sizeof(f));
    fo(i,1,n)f[i][0]=0;
    fo(i,1,n){
        bot[i]=1;top[i]=0;
        memset(sum,0,sizeof(sum));
        fo(j,0,i-1)sum[rak[j]]++;
        fu(j,n,0)sum[j]+=sum[j+1];
        fu(j,i-1,0)rk[j]=sum[rak[j]],wo[rk[j]]=j;
        fo(w,1,i){
            int j=wo[w];//cout<<j<<" "<<pre[j]<<" "<<f[i][j]<<endl;
            if(top[i]>=bot[i]&&pre[j]==pre[sta[i][top[i]]]){
                if(f[i][j]>=f[i][sta[i][top[i]]])top[i]--;
                else continue;
                //continue;
            }
            //cout<<"bot & top "<<bot[i]<<" "<<top[i]<<endl;
            while(bot[i]<top[i]&&get(i,sta[i][top[i]-1],sta[i][top[i]])<=get(i,sta[i][top[i]],j))
                top[i]--;//cout<<"xl "<<get(i,sta[i][top[i]-1],sta[i][top[i]])<<" "<<get(i,sta[i][top[i]],j)<<endl;
            sta[i][++top[i]]=j;
        }
        //cout<<endl<<"sta"<<" ";
        //fo(j,bot[i],top[i])cout<<sta[i][j]<<" ";
        //cout<<endl;
        memset(sum,0,sizeof(sum));
        fo(j,i+1,n)sum[rak[j]]++;
        fo(j,1,n)sum[j]+=sum[j-1];
        fu(j,n,i+1)rk[j]=sum[rak[j]]--,wo[rk[j]]=j;
        fo(w,1,n-i){
            int k=wo[w];//cout<<pre[k]<<" ";
            while(bot[i]<top[i]&&get(i,sta[i][bot[i]],sta[i][bot[i]+1])>=1.0*(pre[k]-pre[i]))bot[i]++;
            f[k][i]=f[i][sta[i][bot[i]]]+(pre[k]-pre[i])*(pre[i]-pre[sta[i][bot[i]]]);
            //cout<<k<<" "<<i<<" "<<sta[i][bot[i]]<<" "<<f[k][i]<<" "<<f[i][sta[i][bot[i]]]<<" "<<pre[k]-pre[i]<<" "<<pre[i]-pre[sta[i][bot[i]]]<<endl;
        }
        //cout<<endl<<endl;
        //fo(j,0,i-1)cout<<"F"<<j<<" "<<f[i][j]<<endl;
        //cout<<endl;
    }
    fo(i,0,n)ans=max(ans,f[n][i]);
    printf("%lld",ans);
}
posted @ 2021-09-25 19:39  fengwu2005  阅读(46)  评论(0编辑  收藏  举报