2017.11.09离线赛总结

kth ——3820

思路:这是一道比较玄学的题目。
首先,此题一定是二分答案,且它的上界应为k,而前缀和直接维护仅只有90。

不难发现每个数的方案数应该与它因子有关,
xy=i ,不妨令 x<y ,那么可以有 x[1,sqrt(i)] , y[sqrt(i),i]
可以简单地打表出x,y的分布会长成这样:
yi,ij,ij,...2,2,2,...,2,1,1,1,1,1...,1
x1,i/(ij),i/(ij),...,i/2,i/2,...,i/2,i,i,i,...,i

那么就可以“跳区间”似的找第k大的数,因为当我们知道 x=num ,一定是先知道它的L,那么它的 R=pos[i/(num+1)]1

code

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define db double
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define pb push_back


#define M 1000005

int n,m,q,k;
int A[M];
LL cnt[M];

struct p20{
    void solve(){
        int cnt=0;
        int w=max(n,m);
        REP(i,1,n){
            REP(j,1,m){
                if(i*j>w)break;
                A[++cnt]=i*j;
            }
        }
        sort(A+1,A+1+cnt);
        while(q--){
            scanf("%d",&k);
            printf("%d\n",A[k]);
        }
    }
}p20;

struct p100{
    LL check(int x){
        int t=sqrt(x);
        LL sum=0;
        REP(i,1,min(t,n))sum+=x/i;
        REP(i,1,min(t,m))sum+=x/i;
        return sum-t*t;
    }
    void solve(){
        while(q--){
            scanf("%d",&k);
            int L=1,R=k,ans;
            while(L<=R){
                int mid=(L+R)>>1;
                if(check(mid)>=k)ans=mid,R=mid-1;
                else L=mid+1;
            }
            printf("%d\n",ans);
        }
    }
}p100; 

int main(){
//  freopen("kth.in","r",stdin);
//  freopen("kth.out","w",stdout);
    cin>>n>>m>>q;
    if(n<=1000 && m<=1000)p20.solve();
    else p100.solve();
    return 0;
}

num ——3821

思路:这是一道十分水的题目,只是考试时没有去想正解,太关心大暴力了…
此题唯一的烦点就在m次询问,且每次标记一个点,而且m的范围蛮大的。

那么我们究其根本,dp的转移只是它的下方和它的下右方,但这层的dp又是从它的上方和它的上左方收集上去的。

也就是说,每一层的转移只与它的上下四个位置有关。
而针对每个标记的 x y ,我们一定是可以先找出一条原先最优的路,看标记的这个点是否在这条路径上。
若没有,很简单,输出原来路径的最大值;否则,则一定是找一条次优的路径,而这条次优的路径和原路径不同只是在于x层的原路径的点和我们要找的另一个次优的点。

那么就简单了,我们就只需要预处理出每一层的最优路径和次优路径,且标次层最优路径的点和次优路径的点即可。

code

#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define db double
#define Sz(a) sizeof(a)
#define mcl(a,b) memset(a,b,Sz(a))
#define mcp(a,b) memcpy(a,b,Sz(b))
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
#define pb push_back

#define N 1005

int n,m;
LL A[N][N];

struct p40{
    #define M 55
    bool mark[M][M];
    LL A[M][M],B[M][M];
    LL work(){
        mcp(B,A);
        DREP(i,n-1,1){
            REP(j,1,i){
                if(mark[i][j])continue;
                if(mark[i+1][j])B[i][j]+=B[i+1][j+1];
                else if(mark[i+1][j+1])B[i][j]+=B[i+1][j];
                else B[i][j]+=max(B[i+1][j],B[i+1][j+1]);
            }
        }
        return B[1][1];
    }
    void solve(){
        REP(i,1,n)REP(j,1,i)scanf("%lld",&A[i][j]);
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==1 && y==1)puts("-1");
            else {
                mark[x][y]=1;
                printf("%lld\n",work());
                mark[x][y]=0;
            }
        }
    }
}p40;

struct p10{
    int s;
    bool check(){
        s=A[1][1];
        REP(i,2,n)REP(j,1,i)if(s!=A[i][j])return 0;
        return 1;
    }
    void solve(){
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==1 && y==1)puts("-1");
            else printf("%lld\n",1LL*n*s);
        }
    }
}p10;

struct p20{
    bool check(){
        REP(i,1,n)REP(j,1,i)if(A[i][j]!=i-j)return 0;
        return 1; 
    }
    void solve(){
        LL ans=1LL*n*(n-1)/2;
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==1 && y==1)puts("-1");
            else {
                if(y==1)printf("%lld\n",ans-n+x-1);
                else printf("%lld\n",ans);  
            }
        }
    }
}p20;

struct p30{
    bool check(){
        REP(i,1,n)REP(j,1,i)if(A[i][j]!=1LL*i*j)return 0;
        return 1; 
    }
    LL sum[N];
    LL calc(){
        LL res=0;
        REP(i,1,n)res+=1LL*i*i,sum[i]=sum[i-1]+i;
        return res;
    }
    void solve(){
        LL ans=calc();
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==1 && y==1)puts("-1");
            else {
                if(x!=y)printf("%lld\n",ans);
                else printf("%lld\n",ans-(sum[n]-sum[x-1]));
            }
        }
    }
}p30;

struct p100{
    LL dp1[N][N],dp2[N][N];
    LL ans[N][2],Id[N][2];//max_id and second max_id
    void Init(){
        REP(i,1,n)
            REP(j,1,i)
                dp1[i][j]=max(dp1[i-1][j-1],dp1[i-1][j])+A[i][j];

        REP(i,1,n)dp2[n][i]=A[n][i];
        DREP(i,n-1,1)
            REP(j,1,i)
                dp2[i][j]=max(dp2[i+1][j],dp2[i+1][j+1])+A[i][j];

        REP(i,1,n){
            REP(j,1,i){
                LL now=max(dp1[i-1][j-1],dp1[i-1][j])+max(dp2[i+1][j],dp2[i+1][j+1])+A[i][j];
                if(now>=ans[i][0]){
                    ans[i][1]=ans[i][0];
                    Id[i][1]=Id[i][0];
                    ans[i][0]=now;
                    Id[i][0]=j;
                }
                else if(now>=ans[i][1]){
                    ans[i][1]=now;
                    Id[i][1]=j;
                }
            }
        }
    }
    void solve(){
        Init();
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x==1 && y==1)puts("-1");
            else printf("%lld\n",y==Id[x][0]?ans[x][1]:ans[x][0]);
        }
    }
}p100;

int main(){
//  freopen("num.in","r",stdin);
//  freopen("num.out","w",stdout);
    cin>>n>>m;
    if(n<=50)p40.solve();
    else {
        REP(i,1,n)REP(j,1,i)scanf("%lld",&A[i][j]);
        if(p10.check())p10.solve();//same 
        else if(p20.check())p20.solve();//i-j
        else if(p30.check())p30.solve();// i*j
        else p100.solve();
    }
    return 0;
}

小结:今天的第3题失误太大了…根本没有去想正解,导致看到第3题无脑敲部分分,导致两个部分分的题意看错了…T_T——看完题先想一波暴力,敲完暴力,再花10分钟思考一下正解。

你可能感兴趣的:(离线赛-总结)