2020.1.11 考试总结

T1 P3537 [POI2012]SZA-Cloakroom

有n件物品,每件物品有三个属性a[i], b[i], c[i] (a[i]<b[i])。

再给出q个询问,每个询问由非负整数m, k, s组成,问是否能够选出某些物品使得:

  1. 对于每个选的物品i,满足a[i]<=m且b[i]>m+s。
  2. 所有选出物品的c[i]的和正好是k。

输入输出样例

输入 #1

5
6 2 7
5 4 9
1 2 4
2 5 8
1 3 9
5
2 7 1
2 7 2
3 2 0
5 7 2
4 1 5

输出 #1

TAK
NIE
TAK
TAK
NIE

正解

将物品按照 a 排序 , 询问按照 m 排序。

这样按顺序枚举询问 , 不断添加物品。之后检查是否合法。

怎么检查呢? 设 f[k] 表示凑到k这个体积 , 所用的b的最小值 , 不断取最小值的最大值 , 进行更新即可 。

考试时只拿了40分的暴力 , 完全没有想到正解。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int LIM = 1<<19;
char buf[1<<20] , *f1 = buf , *f2 = buf , pbuf[1<<20] , *f3 = pbuf , sta[1<<5];
#define gc() ((f1 == f2 && (f2 = (f1 = buf) + fread(buf , 1 , sizeof buf , stdin) , f1 == f2)) ? EOF : *f1++)
#define Over() fwrite(pbuf , 1 , f3 - pbuf , stdout)
inline int read()
{
    register int x = 0; register char c = gc();
    while(c < '0' || c > '9') c = gc();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48) , c = gc();
    return x;
}
inline void Pint(int x)
{
    if(f3 - pbuf > LIM) Over();
    if(!x) { *f3 ++ = '0' , *f3++ = '\n'; return ; }
    if(x < 0) *f3 ++ = '-' , x = -x;
    char *top = sta;
    while(x) { *top ++ = x % 10 + 48 , x /= 10; }
    while(top != sta) *f3++ = *--top; 
    *f3++ = '\n'; return ;
}
const int N = 1100;
int n , Q;
int ail[N*100] , ans[N*N];
struct node { int c , a , b; } w[N];
struct opt{ int m , k , s , id; } r[N*N];
inline bool cmp1(const node &A , const node &B) { return A.a < B.a; }
inline bool cmp2(const opt  &A , const opt  &B) { return A.m < B.m; }

int main()
{
    freopen("a.in" , "r" , stdin);
    freopen("a.out" , "w", stdout);
    n = read();
    for(int i = 1 ; i <= n ; ++i) w[i].c = read() , w[i].a = read() , w[i].b = read();
    Q = read();
    for(int i = 1 ; i <= Q ; ++i) r[i].m = read() , r[i].k = read() , r[i].s = read() , r[i].id = i;
    sort(w + 1 , w + 1 + n , cmp1); sort(r + 1 , r + 1 + Q , cmp2);
    ail[0] = 1e9;
    for(int i = 1 , j = 1 ; i <= Q ; ++i)
    {
        while(j <= n && w[j].a <= r[i].m)
        {
            for(int k = 100000 ; k >= w[j].c ; --k)
                ail[k] = max(ail[k] , min(ail[k-w[j].c] , w[j].b));
            j++;
        }
        if(ail[r[i].k] > r[i].m + r[i].s) ans[r[i].id] = 1;
    }
    for(int i = 1 ; i <= Q ; ++i) puts(ans[i] ? "TAK" : "NIE");
    fclose(stdin); fclose(stdout);
    return 0;
}

T2 P1848 [USACO12OPEN]书架Bookshelf

当农夫约翰闲的没事干的时候,他喜欢坐下来看书。多年过去,他已经收集了 N 本书 (1 <= N <= 100,000), 他想造一个新的书架来装所有书。

每本书 i 都有宽度 W(i) 和高度 H(i)。书需要按顺序添加到一组书架上;比如说,第一层架子应该包含书籍1 ... k,第二层架子应该以第k + 1本书开始,以下如此。每层架子的总宽度最大为L(1≤L≤1,000,000,000)。每层的高度等于该层上最高的书的高度,并且整个书架的高度是所有层的高度的总和,因为它们都垂直堆叠。

请帮助农夫约翰计算整个书架的最小可能高度。

有N(1 <= N <= 100000)本书,每本书有一个宽度W(i),高度H(i),(1 <= H(i) <= 1,000,000; 1 <= W(i) <= L)。

现在有足够多的书架,书架宽度最多是L (1 <= L <= 1,000,000,000),把书按顺序(先放1,再放2.....)放入书架。某个书架的高度是该书架中所放的最高的书的高度。

将所有书放入书架后,求所有书架的高度和的最小值?

输入格式

第一行:两个数N和L

接下来N行每行两个数Hi和Wi

输出格式

一个数,书架高度的最小值

输入输出样例

输入 #1

5 10
5 7
9 2
8 5
13 2
3 8

输出 #1

21

正解

首先单调栈处理出 i 这个位置之前第一个比他大的位置 , 记为 pre , 那么 pre+1 , i 这段区间内的最大值就是 hi

用线段树维护所有数到 i 的最大的 h 以及这个点的dp值 , 具体的只用维护两个东西 ,

一个是 f 也就是这个点的dp值

另一个是 f+maxh 。然后就没了 。

考试时还算是中规中矩吧 , 但是暴力分也没有拿到满分。。。

改题时 , 居然没有建树赋初值。。。。。。。。。。。。

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 101000;
const long long inf = 1e18;
char buf[1<<20] , *f1 = buf , *f2 = buf;
#define gc() (f1 == f2 && (f2 = (f1 = buf) + fread(buf , 1 , sizeof buf , stdin) , f1 == f2) ? EOF : *f1++)
inline long long read()
{
    register long long x = 0; register char c = gc();
    while(c < '0' || c > '9') c = gc();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48) , c = gc();
    return x;
}
int n , L , top;
int pre[N] , sta[N];
long long sum[N] , h[N] , w[N] , tr[N<<2] , f[N<<2] , tag[N<<2] , dp[N];
#define lson k<<1 , l , mid
#define rson k<<1|1 , mid+1 , r

inline void update(int k) 
{ 
    f[k] = min(f[k<<1] , f[k<<1|1]); 
    tr[k] = min(tr[k<<1] , tr[k<<1|1]); 
    return ; 
}
inline void down(int k)
{
    if(tag[k] == inf) return ;
    tr[k<<1] = f[k<<1] + tag[k];
    tr[k<<1|1] = f[k<<1|1] + tag[k];
    tag[k<<1] = tag[k<<1|1] = tag[k]; 
    tag[k] = inf; return ; 
}

void build(int k , int l , int r)
{
    tr[k] = f[k] = tag[k] = inf;
    if(l == r) return ; int mid = (l + r) >> 1;
    build(lson); build(rson); return ;
}

void Insert(int k , int l , int r , int pos)
{
    if(l == r) { f[k] = dp[pos-1]; tr[k] = inf; return ;}
    int mid = (l + r) >> 1; down(k);
    return (void)(pos <= mid ? Insert(lson , pos) : Insert(rson , pos));
}

void Change(int k , int l , int r , int x , int y , int val)
{
    if(x <= l && r <= y) { tr[k] = f[k] + val; tag[k] = val; return ;}
    int mid = (l + r) >> 1; down(k);
    if(x <= mid) Change(lson , x , y , val);
    if(y  > mid) Change(rson , x , y , val);
    update(k); return ;
}

long long Ask(int k , int l , int r , int x , int y)
{
    if(x <= l && r <= y) return tr[k];
    int mid = (l + r) >> 1; down(k);
    long long ans = inf;
    if(x <= mid) ans = min(ans , Ask(lson , x , y));
    if(y  > mid) ans = min(ans , Ask(rson , x , y));
    return ans;
}

int main()
{
    // freopen("b.in" , "r" , stdin);
    // freopen("b.out" , "w" , stdout);
    n = read(); L = read();
    for(int i = 1 ; i <= n ; ++i) h[i] = read() , w[i] = read() , sum[i] = sum[i-1] + w[i];
    for(int i = n ; i >= 1 ; --i)
    {
        while(top && h[sta[top]] < h[i]) pre[sta[top--]] = i;
        sta[++top] = i;
    }
    build(1 , 1 , n);
    for(int i = 1 ; i <= n ; ++i)
    {
        Insert(1 , 1 , n , i);
        Change(1 , 1 , n , pre[i] + 1 , i , h[i]);
        int pos = lower_bound(sum , sum + 1 + i , sum[i] - L) - sum;
        if(pos < i) dp[i] = Ask(1 , 1 , n , pos+1 , i);
    }
    printf("%lld\n" , dp[n]);
    fclose(stdin); fclose(stdout);
    return 0;
}

T3 P3302 [SDOI2013]森林

题目描述

小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有M条边。

小Z希望执行T个操作,操作有两类:

  1. Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。
  2. L x y在点x和点y之间连接一条边。保证完成此操作后,仍然是一片森林。

为了体现程序的在线性,我们把输入数据进行了加密。设lastans为程序上一次输出的结果,初始的时候lastans为0。

  • 对于一个输入的操作Q x y k,其真实操作为Q x^lastans y^lastans k^lastans
  • 对于一个输入的操作L x y,其真实操作为L x^lastans y^lastans。其中^运算符表示异或,等价于pascal中的xor运算符。

请写一个程序來帮助小Z完成这些操作。

对于所有的数据,\(n,m,T<=8∗10^4\)

输入格式

第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1<=testcase<=20。

第二行包含三个整数N,M,T,分别表示节点数、初始边数、操作数。

第三行包含N个非负整数表示 N个节点上的权值。

接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边。

接下来 T行,每行描述一个操作,格式为”Q x y k“或者”L x y “,其含义见题目描述部分。

输出格式

对于每一个第一类操作,输出一个非负整数表示答案。

输入输出样例

输入 #1

1
8  4 8
1  1 2 2 3 3 4 4
4  7
1  8
2  4
2  1
Q 8 7 3 Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 L 0 7
Q 9 2 5 Q 6 1 6

输出 #1

2 
2
1
4
2

说明/提示

对于第一个操作 Q 8 7 3,此时 lastans=0,所以真实操作为Q 8^0 7^0 3^0,也即Q 8 7 3。点8到点7的路径上一共有5个点,其权值为4 1 1 2 4。
这些权值中,第三小的为 2,输出 2,lastans变为2。

对于第二个操作 Q 3 5 1 ,此时lastans=2,所以真实操作为Q 3^2 5^2 1^2 ,也即Q 1 7 3。点1到点7的路径上一共有4个点,其权值为 1 1 2 4 。
这些权值中,第三小的为2,输出2,lastans变为 2。之后的操作类似。

img

这个既有连边又有第k大 , 又像lct , 又像主席树, 结果就蒙逼了。

但是这个题没有删边操作 , 也就是只用考虑主席树怎么合并就行了, 可以考虑启发式合并 。

这样就能过了。。

但是。。。。。。。

毒瘤的Treaker大神 ,把空间开到了 128 MB , 导致必须回收节点。。。。。

回收节点要注意,只能回收这一条链,不能把他fa的也给回收了。。。

还得记得建新边 , 建新边还得注意先回收再连边(不然就把整个树都回收了)

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 1e5+10;
inline int read()
{
    register int x = 0 , f = 0; register char c = getchar();
    while(c < '0' || c > '9') f |= c == '-' , c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return x;
}
int n , m , Q , cnt , tot , id , top;
int a[N] , b[N] , head[N] , root[N] , fa[N] , son[N] , siz[N*100] , ls[N*100] , rs[N*100] , d[N] , f[N][18] , vis[N] , sta[N*20];
struct edge{ int v , nex; } e[N<<1];
inline void add(int u , int v) { e[++cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt; return ; }

inline int Newnode() 
{
    int x;
    if(top) x = sta[top--]; else x = ++id;
    siz[x] = ls[x] = rs[x] = 0; return x; 
}

int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }

void Insert(int &x , int y , int l , int r , int pos)
{
    x = Newnode(); siz[x] = siz[y] + 1;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    if(pos <= mid) rs[x] = rs[y] , Insert(ls[x] , ls[y] , l , mid , pos);
    else ls[x] = ls[y] , Insert(rs[x] , rs[y] , mid + 1 , r , pos);
    return ;
}

int Ask(int x , int y , int px , int py , int l , int r , int k)
{
    if(l == r) return b[l];
    int si = siz[ls[x]] + siz[ls[y]] - siz[ls[px]] - siz[ls[py]] , mid = (l + r) >> 1;
    if(si >= k) return Ask(ls[x] , ls[y] , ls[px] , ls[py] , l , mid , k);
    else return Ask(rs[x] , rs[y] , rs[px] , rs[py] , mid+1 , r , k - si);
}

int LCA(int x , int y)
{
    if(x == y) return x;
    if(d[x] < d[y]) swap(x , y);
    for(register int i = 17 ; ~i ; --i) x = (d[f[x][i]] >= d[y]) ? f[x][i] : x;
    if(x == y) return x;
    for(register int i = 17 ; ~i ; --i) if(f[x][i] ^ f[y][i]) x = f[x][i] , y = f[y][i];
    return f[x][0];
}

int Query(int x , int y , int k)
{
    int p = LCA(x , y);
    return Ask(root[x] , root[y] , root[p] , root[f[p][0]] , 1 , tot , k);
}

void dfs(int x , int Fa , int rt)
{
    f[x][0] = Fa; fa[x] = Fa; son[rt]++; d[x] = d[Fa] + 1; vis[x] = 1;
    for(int i = 1 ; i <= 17 ; ++i) f[x][i] = f[f[x][i-1]][i-1];
    Insert(root[x] , root[Fa] , 1 , tot , a[x]);
    for(int i = head[x] , v; i ; i = e[i].nex)
    {
        v = e[i].v; if(v == Fa) continue;
        dfs(v , x , rt);
    }
    return ;
}

void rec(int &x , int y)
{
    if(!x) return ;
    if(ls[x] != ls[y]) rec(ls[x] , ls[y]);
    if(rs[x] != rs[y]) rec(rs[x] , rs[y]);
    sta[++top] = x; x = 0; return ;
}

void Rec(int x , int Fa)
{
    for(int i = head[x] , v; i ; i = e[i].nex)
    {
        v = e[i].v; if(v == Fa) continue;
        Rec(v , x);
    }
    rec(root[x] , root[Fa]);
    return ;
}

void solve()
{
    n = read(); m = read(); Q = read();
    register int i , ans = 0 , x , y , k;
    for(i = 1 ; i <= n ; ++i) a[i] = read() , b[i] = a[i] , fa[i] = i;
    sort(b + 1 , b + 1 + n); tot = unique(b + 1 , b + 1 + n) - b - 1;
    for(i = 1 ; i <= n ; ++i) a[i] = lower_bound(b + 1 , b + 1 + tot , a[i]) - b;
    for(i = 1; i <= m ; ++i)
    {
        x = read(); y = read();
        add(x , y); add(y , x);
    }
    for(i = 1 ; i <= n ; ++i) if(!vis[i]) dfs(i , 0 , i) , fa[i] = i;
    char c[5];
    for(i = 1; i <= Q ; ++i)
    {
        scanf("%s" , c);
        if(c[0] == 'Q')
        {
            x = read() ^ ans; y = read() ^ ans; k = read() ^ ans;
            printf("%d\n" , ans = Query(x , y , k));
        }
        else
        {
            x = read() ^ ans , y = read() ^ ans;
            int fx = find(x) , fy = find(y);
            if(son[fx] < son[fy]) { swap(fx , fy); swap(x , y); }
            Rec(fy , 0); dfs(y , x , fx);
            add(x , y); add(y , x);
        }
    }
    return ;
}

int main()
{
    // freopen("c.in" , "r" , stdin);
    // freopen("std.out" , "w" , stdout);
    int T = read();
    solve();
    fclose(stdin); fclose(stdout);
    return 0;
}
/*
2
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3
Q 3 5 1
Q 10 0 0
L 5 4
L 3 2
L 0 7
Q 9 2 5
Q 6 1 6
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3
Q 3 5 1
Q 10 0 0
L 5 4
L 3 2
L 0 7
Q 9 2 5
Q 6 1 6
*/

猜你喜欢

转载自www.cnblogs.com/R-Q-R-Q/p/12180616.html
今日推荐