17.8.12第六次测试

1.ksum

【问题描述】
Peter喜欢玩数组。NOIP这天,他从Jason手里得到了大小为n的一个正整数
数组。
Peter求出了这个数组的所有子段和,并将这n(n+1)/2个数降序排序,他想
知道前k个数是什么。
【输入格式】
输入文件名为 ksum.in。
输入数据的第一行包含两个整数 n 和 k。
接下来一行包含 n 个正整数,代表数组。
【输出格式】
输出文件名为 ksum.out。
输出 k 个数,代表降序之后的前 k 个数,用空格隔开。
【输入输出样例】
ksum.in 
3 4
1 3 4
ksum.out
8 7 4 4

ksum.in 
3 3
10 2 7
ksum.out
19 12 10

【数据规模与约定】
对于所有数据,满足 ai≤10
9 k≤n(n+1)/2,n≤100000,k≤100000
测试点编号 n ≤           k ≤
1              100          5000
2              500          100000
3              1000        80000
4              1000        100000
5              10000      50000
6              20000      80000
7              50000      80000
8              100000    80000
9              100000    100000
10            100000    100000
View Code

题解:算是个模拟题吧。最大字段肯定是整个数列。进行如下操作:取出当前最大子段,然后放入这个子段的左端-1和其右端-1两个字段(这两段肯定是它之内比它小的最大子段了)重复直到结束。脑补一下就可以出来,或者手推。这里用到一个叫set的东西非常好,因为可能从两个子段同时得到同一个小子段,所以会有重复情况,为了避免这个情况,set就行。set集合其实就相当于自动去重的优先队列,自动避免了上述情况。更多set的东西可以百度。对了,结构体用set需要用重载运算符重载"<"。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
#define ll long long
struct num{
    ll l,r,val;
    bool operator<(const num &b)const{
        num a=*this;
        if(a.val!=b.val) return a.val<b.val;
        if(a.l != b.l) return a.l < b.l;
        return a.r < b.r;
    }
}e[1000100];
set<num>q;
long long a[100010],sum,cnt;
int n,k,t;
int main(){
    freopen("ksum.in","r",stdin);
    freopen("ksum.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%I64d",&a[i]);
        sum+=a[i];
    }
    e[++cnt].l=1;
    e[cnt].r=n;
    e[cnt].val=sum;
    q.insert(e[1]);
    while(k--){
        num x=*q.rbegin();q.erase(x);
        printf("%I64d ",x.val);
        if(x.l<x.r){
            e[++cnt].l=x.l+1;
            e[cnt].r=x.r;
            e[cnt].val=x.val-a[x.l];
            q.insert(e[cnt]);
            e[++cnt].l=x.l;
            e[cnt].r=x.r-1;
            e[cnt].val=x.val-a[x.r];
            q.insert(e[cnt]); 
        }
    }
    return 0;    
}
View Code

 

 

 

2.奇袭

【问题描述】
由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上
要迎来最终的压力测试——魔界入侵。
唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量
是远远不够的。所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族。
在UW的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵
前发动一次奇袭,袭击魔族大本营!
为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔
族大本营进行侦查,并计算出袭击的难度。
经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N
×N的网格图,一共有N支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。
在大本营中,每有一个k×k(1≤k≤N)的子网格图包含恰好k支军队,我们
袭击的难度就会增加1点。
现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。
【输入格式】
第一行,一个正整数N,表示网格图的大小以及军队数量。
接下来N行,每行两个整数,Xi,Yi,表示第i支军队的坐标。
保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样
的。
【输出格式】
一行,一个整数表示袭击的难度。
【输入输出样例】
raid.in 
5
1 1
3 2
2 4
5 5
4 3

raid.out
10
【样例解释】
显然,分别以(2,2)和(4,4)为左上,右下顶点的一个子网格图中有3支军队,
这为我们的难度贡献了1点。
类似的子网格图在原图中能找出10个。
【数据范围】
对于30%的数据,N ≤ 100
对于60%的数据,N ≤ 5000
对于100%的数据,N ≤ 50000
View Code

题解:二维压一维后,题目可化简为:给定 N 个数的一个排列,问这个序列中有多少个子区间的数恰好是连续的。这个画图可得。

进一步可以化为:有多少种情况使得,相邻的 k 个数中最大值和最小值的差小于等于 k-1。

大致有两种解法,一种是分治,一种是线段树。
这里主要讲一下分治的解法。
考虑分治,对于当前分治区间[L,R],记区间中点为 mid。当
前区间的答案就是Ans[L..mid]+Ans[mid+1..R]+跨过中点的合
法区间数,然后就分为两种情况了:
1.最小值和最大值在同侧。
2.最小值和最大值在异侧。
下面只考虑最值同在左,和最小值在左,最大值在右的情况。
其余两种是对称的。
对于最值同在左侧的情况,我们枚举左边界在哪,然后可以计算出右边界的位置,在判断是否合法,统计答案。

时间复杂度:
O(N).对于最小值在左侧,最大值在右侧的情况,如果一个区间满足我们所要求的关系的话话,就一定有: max(a[mid 1]...a[right])  min(a[left]...a[mid]) = right -left

移项可得

max(a[mid 1]...a[right]) -right = min(a[left]..a[mid])-left然后可以用单调栈+桶来完成这个任务。时间复杂度:O(N). 如果加一些黑科技可以大大减少代码量,但是复杂度会多一 个 log。 总的时间复杂度:O(NlogN)/O(NlogN^2) 简单提一下,线段树解法的思路大致也是维护一个单调栈, 然后进行区间修改和查询,统计答案。 时间复杂度:O(NlogN).

代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;

#define N 600000 + 10
typedef long long ll ;
int TAX[N] ;
int A[N] ,lmax[N] , lmin[N] , rmax[N] , rmin[N] ,a,b;
int *tax = TAX + 300005 ;
int n ;

ll cal(int l,int r,int m){
    ll ret=0;
    lmax[m]=lmin[m]=A[m];
    rmax[m+1]=rmin[m+1]=A[m+1];
    for(int i=m-1;i>=l;i--){
        lmax[i] = max( lmax[i+1] , A[i] ) ;
        lmin[i] = min( lmin[i+1] , A[i] ) ;
    }
    for(int i=m+2;i<=r;i++){
        rmax[i] = max( rmax[i-1] , A[i] ) ;
        rmin[i] = min( rmin[i-1] , A[i] ) ;
    }
    for(int i=l;i<=m;i++){
        int j=lmax[i]-lmin[i]+i;
        if(rmax[j]<lmax[i]&&rmin[j]>lmin[i]&&j>m){
            ret++;
        }
    }
    int p=m+1,q=m;
    while(q<r&&rmin[q+1]>lmin[l])q++,tax[rmax[q]-q]++;
    while(p<=r&&rmax[p]<lmax[l])tax[rmax[p]-p]--,p++;
    for(int i=l;i<=m;i++){
        while(p>m+1&&rmax[p-1]>lmax[i])p--,tax[rmax[p]-p]++;
        while(q>m&&rmin[q]<lmin[i])tax[rmax[q]-q]--,q--;
        ret+=max(tax[lmin[i]-i],0);
    }
    for (int i = m + 1 ; i <= r ; i ++ ) tax[rmax[i]-i] = 0 ;
    return ret;
}

ll solve(int l,int r){
    if(l==r)return 1ll;
    int m=(l+r)>>1;
    ll ret=0;
    ret+=solve(l,m);
    ret+=solve(m+1,r);
    ret+=cal(l,r,m);
    reverse (A+l,A+r+1);
    if((r-l+1)%2)m--;
    ret+=cal(l,r,m);
    reverse(A+l,A+r+1);
    return ret;
}

int main(){
    freopen( "raid.in" , "r" , stdin ) ;
    freopen( "raid.out" , "w" , stdout ) ;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a,&b);
        A[a]=b;
    }
    printf("%I64d\n",solve(1,n));
    return 0;
} 
View Code

 

 

 

3.十五数码

【题目描述】
给出起始顺序,要求通过 0 的移动(与上下左右交换),排成以下顺序:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 0
【输入格式】
从文件 fiften.in 中读入数据,四个数一行,共四行。
【输出格式】
输出到文件 fifteen.out 中。
输出最少移动次数。如果无解输出 No。
【样例 1 输入】
1 2 3 4
5 6 7 8
9 10 11 12
13 14 0 15
【样例 1 输出】
1
【样例 2 输入】
1 11 3 8
5 7 0 2
9 13 4 12
6 10 14 15
【样例 2 输出】
33
【数据范围】
对于 20%的数据,保证有解并且 Ans <= 12;
对于 50%的数据,保证有解并且 Ans <= 28;
存在 10%的数据无解。
对于 100%的数据,如果有解,Ans <= 50;
View Code

题解:丧病的八数码的升级版。然而当时我连八数码都不会打。orz。

就是搜,使劲搜。IdA*算法的搜。

这题时限相当坑,两个点死活过不去,最后加长到三秒了才过orz。

这题的check函数判断无解,就是数列转换为一维后,如果包含0在内计算每一个数之前小于自己的数(含0)的个数之和加上0当前位置到其应在位置的曼哈顿距离为奇数则有解,反之无解。

剩下就是搜索,界限bound为搜索深度,每次更新为大于当前值最小的(step+h)step为当前步数,h是估价函数。

h是当前每个数到其应在位置的曼哈顿距离和。(不含0)

差不多就这些。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char str[10];
int xx[17],yy[17],bound,flg;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
char s[4] ={'l','r','u','d'};
int mm,xq,yq,r;
inline int caldis(int aa,int x,int y)
{
    return abs(xx[aa]-x) + abs(yy[aa]-y);
}

struct node
{
    int pos,mat[20];
    char out[100];
    int H()
    {
        register int ret = 0;
        for(register int i = 0; i < 15; i ++)
            ret += abs(xx[mat[i]] - (i>>2)) + abs(yy[mat[i]] - (i&3));
        return ret;
    }
    bool check()
    {
        int tot = 0;
        
        for(int i = 0; i < 16; i ++){
            if(!mat[i]) continue;
            for(int j = 0; j < i; j ++)
                if(mat[j] < mat[i]) tot++;
        }
        tot+=mm;
        if(!(tot&1)) return false;
        else return true;
    }
    void output()
    {
        int len =strlen(out);
        for(int i = len-1; i >=0; i  --)
            putchar(out[i]);
    }
}a;

bool valid(int x,int y)
{
    return 0 <= x && x <= 3 && 0 <= y && y <= 3;
}

bool ok(int aa,int bb)
{
    if(aa > bb) swap (aa,bb);
    if(aa==0&&bb==1) return false;
    if(aa==2&&bb==3) return false;
    return true;
}

int dfs(register int step,register int h,register int las)
{
    if(step + h > bound) return step + h;
    if(!h)
    {
        flg=1;
        return step;
    }
    register int pos =a.pos;
    int x = (a.pos>>2),y = (a.pos&3),ret =127;
    for(int k = 0; k < 4; k ++)
    {
        int tx = x + dx[k];
        int ty = y + dy[k];
        if(!valid(tx,ty)||!ok(k,las)) continue;
        int tar = (tx<<2) + ty;
        swap(a.mat[pos],a.mat[tar]);
        a.pos = tar;
        int ht = h - caldis(a.mat[pos],tx,ty) + caldis(a.mat[pos],x,y);
        int tmp = dfs(step+1,ht,k) ;
        if(flg) return tmp;if(ret>tmp)ret = tmp;
        swap(a.mat[pos],a.mat[tar]);
        a.pos = pos;
    }
    return ret;
}

int main()
freopen("fifteen.in","r",stdin);
freopen("fifteen.out","w",stdout);
{
    for(int i = 0; i < 16; ++i){
        scanf("%d",&r);
        if(r==0) {
            a.pos = i;
            xq = (i>>2); yq = (i&3);
            mm=3-xq+3-yq;
        }
        else
        {

            a.mat[i] = r;
            xx[r] = (i>>2); yy[r] = (i&3);
        }
    }
    if(!a.check()) 
    {
        printf("No\n");
        return 0;
    }
    for(int i = 0; i < 15; ++i) a.mat[i] = i + 1; a.mat[15] = 0;
    a.pos = 15;
    for(bound = a.H(); bound <= 55&&!flg ; bound = dfs(0,a.H(),4) );
    if(!flg)
    {
            printf("No\n");
        return 0;
    }
    printf("%d",bound);
}
View Code

 

posted @ 2017-08-19 21:30  Requiescat  阅读(365)  评论(0编辑  收藏  举报