关闭

六一儿童节题解QAQ

120人阅读 评论(0) 收藏 举报
分类:

T1:
有n种方块,每种方块有权值和个数。
每次寻找区间[l,r]的权值范围在[a,b]的方块中,选k个搭积木。
问每个积木的最大的权值最小是多少。
n,m<=30000
题解:
问最大的权值最小不就是……
排个序之后的第k个,就是最大的最小权值了啊……
然后所以我们考虑这个东西不就是有权值范围限制的第k大吗……
这不是裸的可持久化线段树吗……
随便搞搞就好了。

#include <algorithm>
#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define u t[x]
#define o t[y]
#define mid (l + r >> 1)
#define lson u.lc,l,mid
#define rson u.rc,mid + 1,r
using namespace std;
const int N = 30005;
const int NN = ((1 << 30) - 5);
int tot = 0,n,m,ql,qr,qwl,qwr,K,rt[N];
struct Seg{int lc,rc,cnt;}t[N << 5];
void Ins(int &x,int l,int r,int val,int ct,int y)
{
    if(!x)x = ++ tot;
    u.cnt = o.cnt;
    u.cnt += ct;
    if(l == r)return;
    if(val <= mid)Ins(lson,val,ct,o.lc),u.rc = o.rc;
    else Ins(rson,val,ct,o.rc),u.lc = o.lc;
}
int Qry_val(int x,int l,int r,int y)
{
    if(l >= qwl && r <= qwr)
        return u.cnt - o.cnt;
    int a1 = 0,a2 = 0;
    if(qwl <= mid)a1 = Qry_val(lson,o.lc);
    if(mid < qwr)a2 = Qry_val(rson,o.rc);
    return a1 + a2;
}
int Qry(int x,int l,int r,int y)
{
    if(l == r)return l;
    int Now = t[u.lc].cnt - t[o.lc].cnt;
    if(Now < K)return K -= Now,Qry(rson,o.rc);
    else return Qry(lson,o.lc);
}
int main ()
{
    freopen("building.in","r",stdin);
    freopen("building.out","w",stdout);
    scanf("%d",&n);
    Rep(i,n)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        Ins(rt[i],1,NN,a,b,rt[i - 1]);
    }
    scanf("%d",&m);
    Rep(i,m)
    {
        scanf("%d%d%d%d%d",&ql,&qr,&qwl,&qwr,&K);
        int cpt = Qry_val(rt[qr],1,NN,rt[ql - 1]); 
        if(cpt < K){puts("-1");continue;}
        swap(qwl,qwr),qwl = 1,qwr --;
        int cpp;
        if(qwl > qwr)cpp = 0;
        else cpp = Qry_val(rt[qr],1,NN,rt[ql - 1]); 
        K = cpp + K;
        printf("%d\n",Qry(rt[qr],1,NN,rt[ql - 1]));
    }
    return 0;
}

T2:
给一个点带权有向图,每个点有花费,你需要选择一些点,使得拆掉这些点之后,不能从入度为0的点到达出度为0的点。
求最小的花费总和。
n<=1000,m<=6000
题解:
炸了。
T3:
给一个数列,数列相当于一条彩带,可以选择把数列的某个位置x切开,拼接a[x,n]和a[1,x - 1]。
求拼接之后的最小字典序的数列。
例子:4 3 2 1
答案:1 4 3 2
题解:
最小表示法。
我们用两个指针去扫,假设:
s[i - > i + k - 1] == s[j - > j + k - 1]
s[i + k] > s[j + k]
则:
考虑i有没有可能是最终答案:
显然不可能对吧,因为从j开始就比i优。
那么i + 1可不可能是最终答案呢?
显然j + 1比i + 1还要优。
所以s[i + k + 1]之后的才有可能作为最终答案。
我们就直接把i跳到i + k + 1的部分就成了。
总的复杂度显然是O(n)的。

#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
using namespace std;
int s[300005],n;
int Get(int *a)
{
    int i = 1,j = 2,k = 0,len = n;
    while(i <= n && j <= n && k <= n)
    {
        if(k == n)return i;
        if(i == j)j ++;
        int ni = i + k,nj = j + k;
        if(ni > len)ni -= len;
        if(nj > len)nj -= len;
        if(a[ni] > a[nj])i += k + 1,k = 0;
        else if(a[nj] > a[ni])j += k + 1,k = 0;
        else k ++;
    }
    return i;
}
int main ()
{
    freopen("work.in","r",stdin);
    freopen("work.out","w",stdout);
    scanf("%d",&n);
    Rep(i,n)scanf("%d",&s[i]);
    int pos = Get(s);
    for(int i = pos;i <= n;i ++)
        printf("%d ",s[i]);
    for(int i = 1;i < pos;i ++)
        printf("%d ",s[i]);
    puts("");
    return 0;
}

T3死因:后缀数组写不出来,水不过。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:43838次
    • 积分:1689
    • 等级:
    • 排名:千里之外
    • 原创:128篇
    • 转载:3篇
    • 译文:0篇
    • 评论:41条
    单向膜拜