2.21闲话 & solution『 有没有/谁/能够代替我』

上午有唐氏模拟赛,100/0/0/20rk7/15,鉴定为最唐的一次

T1签到题,思路很简单

  • 题面

    ame 是一个可爱的女孩子,她想要你帮她排序。 给定\(4\times n\)个数,要求将其分为\(n\)组,使得对于每组四个数 ,所有组中 的\(|a\times b-c\times d|\)和最大,求最大和。

排序,对于前\(2n\)大的,尽量把大的和大的相乘,后\(2n\)的把大的和小的相乘,然后两者相减即可

赛时没删sleep的调试,但是我的sleep是用for循环代替的,而我开了O2侥幸通过此题

天依$\text{'s Code}$
int n,a[N],ans;
inline bool cmp(int x,int y){
	return x<y;
}
signed main() {
	FastI>>n;
	for(int i=1;i<=4*n;i++){
		FastI>>a[i];
	}
	sort(a+1,a+1+4*n,cmp);
	const int TianYi=n*2;
	for(int i=4*n;i>TianYi;i-=2){
		ans+=a[i]*a[i-1];
	}
	for(int i=TianYi,j=1;i>=j;i--,j++){
		ans-=a[i]*a[j];
	}
	FastO<<ans;
}

T2是二分+单调队列优化\(\text{dp}\)

image

看到题面直接就知道是二分了,再仔细看看\(dp\)也出来了,但是明显裸的\(dp\)过不去,考虑优化\(\text{dp}\)

观察\(\text{dp}\)的转移方程发现满足单调队列优化的形式,考虑单调队列优化\(\text{dp}\)

天依$\text{'s Code}$
int f[N][2],n,m,s,w[N];
pair<int,int>que[N];
int head=1,tail=0;
inline void clear(){
    head=1,tail=0;
}
inline void change(int x,int id){
    while(head<=tail&&que[tail].first<x) 
        tail--;
    que[++tail]=make_pair(x,id);
}
inline int Get(int l){
    while(head<=tail&&que[head].second<l)     
        head++;
    if(head>tail) 
        return 0;
    return que[head].first;
}
inline bool check(int sz){
    memset(f,0,sizeof(f)); 
    clear();
    for(int i=1;i<=n;i++){
        if(i>=sz) 
            change(max(f[i-sz][1],f[i-sz][0])+n-(i-sz),i-sz);
        f[i][0]=max(f[i-1][1],f[i-1][0]);
        f[i][1]=max(f[i][1],Get(i-w[i])+i-n);
    }
    if(max(f[n][0],f[n][1])<s) return 0;
    else return 1;
}
signed main(){
    FastI>>n;
    for(int i=1;i<=n;i++) FastI>>w[i];
    FastI>>s;s=ceil(n*1.0/100*s);
    int l=1,r=n;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)) l=mid+1;
        else r=mid-1;
    }
    FastO<<r;
}

T3赛时以为是树上组合数学,正解是树状dp

蒜头君有一棵 n 个节点的树(即 n个节点,n−1 条边的无向连通图)。树的每个节点上都有一个宝藏。蒜头君准备大动干戈,拿到这些保证。

但是在拿宝藏之前,蒜头君发现了一个问题,由于树的边的材质问题,若两个节点被一条边直接连接,为了确保安全,那么这两个节点上的宝藏最多可以拿一个。

好在同样擅长化学的巨佬--花椰妹给了蒜头君一条特殊材质的边。蒜头君可以选定一条边并将这条边的材质替换成特殊材质的边,于是为了确保安全,被这条选定的边直接相连的两个节点上的宝藏最少拿一个。

蒜头君想知道,对于每一条边,若选定这条边替换成花椰妹送给他的特殊材质的边,在确保安全的情况下,有多少种拿的方法是可行的。

查看错误题解

image

但是题解是错误的,因为裸的树形dp复杂度是\(O(n^2)\)无法通过此题

可以通过dfs预处理降低复杂度,详情看DZ博客

唔,代码没有诶

T4赛时打了暴力,然后去写题了

正解是二分+线段树+扫描线,官方题解依然是过于简略

image

查看过于简略的题解

image

这里是正确的题解

image

点击查看std
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
int n, m;
int arr[MAXN];
int last[MAXN];
int nxt[MAXN];
long long answer[MAXN];

struct SegmentTree {
public:
    int min, num;
    long long sum;
    class LazyTag {
    public:
        int cover, tim;
        long long add;
        inline LazyTag() {
            cover = -1;
            add = 0;
            tim = 0;
        }
        inline LazyTag(const int cover): cover(cover) {
            if (cover == -1) {
                tim = 1;
            }
        }
    } tag;
} sgt[MAXN << 2];

inline void PushUp(const int now) {
    sgt[now].min = min(sgt[now << 1].min, sgt[now << 1 | 1].min);
}
void Build(const int now = 1, const int left = 0, const int right = m) {
    if (left == right) {
        sgt[now].num = sgt[now].min = last[left];
        return;
    }

    Build(now << 1, left, (left + right) >> 1);
    Build(now << 1 | 1, ((left + right) >> 1) + 1, right);
    PushUp(now);
}
inline void Down(const SegmentTree::LazyTag tag,
                 const int now, const int left, const int right) {
    sgt[now].sum += 1ll * sgt[now].num * tag.tim + tag.add;

    if (~tag.cover) {
        if (~sgt[now].tag.cover) {
            sgt[now].tag.add += 1ll * sgt[now].tag.cover * tag.tim + tag.add;
        } else {
            sgt[now].tag.tim += tag.tim;
            sgt[now].tag.add = tag.add;
        }

        sgt[now].num = sgt[now].min = tag.cover;
        sgt[now].tag.cover = tag.cover;
    } else {
        if (~sgt[now].tag.cover) {
            sgt[now].tag.add += 1ll * sgt[now].tag.cover * tag.tim;
        } else {
            sgt[now].tag.tim += tag.tim;
        }
    }
}
inline void PushDown(const int now, const int left, const int right) {
    Down(sgt[now].tag, now << 1, left, (left + right) >> 1);
    Down(sgt[now].tag, now << 1 | 1, ((left + right) >> 1) + 1, right);
    sgt[now].tag = SegmentTree::LazyTag();
}
void Updata(const int now_left, const int now_right,
            const int cover, const int now = 1,
            const int left = 0, const int right = m) {
    if (now_right < left || right < now_left) {
        return;
    }

    if (now_left <= left && right <= now_right) {
        Down(SegmentTree::LazyTag(cover), now, left, right);
        return;
    }

    PushDown(now, left, right);
    Updata(now_left, now_right, cover, now << 1, left, (left + right) >> 1);
    Updata(now_left, now_right, cover,
           now << 1 | 1, ((left + right) >> 1) + 1, right);
    PushUp(now);
}
inline void Time() {
    Down(SegmentTree::LazyTag(-1), 1, 1, m);
}
int Find(const int now_left, const int now_right,
         const int val, const int now = 1,
         const int left = 0, const int right = m) {
    if (now_right < left || right < now_left || val <= sgt[now].min) {
        return -1;
    }

    if (left == right && now_left <= left && right <= now_right) {
        return left;
    }

    int result(Find(now_left, now_right, val, now << 1 | 1,
                    ((left + right) >> 1) + 1, right));
    return ~result ? result : Find(now_left, now_right,
                                   val, now << 1, left, (left + right) >> 1);
}
void GetAnswer(const int now = 1, const int left = 0, const int right = m) {
    if (left == right) {
        answer[left] = sgt[now].sum;
        return;
    }

    PushDown(now, left, right);
    GetAnswer(now << 1, left, (left + right) >> 1);
    GetAnswer(now << 1 | 1, ((left + right) >> 1) + 1, right);
}

int main() {
    scanf("%d%d", &n, &m);
    int last0 = 0;
    long long answer0 = 0;

    for (int i = 1; i <= n; i++) {
        scanf("%d", &arr[i]);

        if (arr[i] == 0) {
            answer0 += (i - last0 - 1ll) * (i - last0) >> 1;
            last0 = i;
        }
    }

    answer0 += (n - last0) * (n + 1ll - last0) >> 1;

    for (int i = 0; i <= m; i++) {
        last[i] = n + 1;
    }

    for (int i = n; i >= 1; i--) {
        nxt[i] = last[arr[i]];
        last[arr[i]] = i;
    }

    for (int i = 1; i <= m - 1; i++) {
        last[i] = max(last[i], last[i - 1]);
    }

    Build();

    for (int i = 1; i <= n; i++) {
        Time();
        int f = Find(arr[i], m, nxt[i]);

        if (~f) {
            Updata(arr[i], f, nxt[i]);
        }
    }

    GetAnswer();
    int l, r;
    scanf("%d%d", &l, &r);

    if (!l) {
        printf("%lld ", answer0);
        l = 1;
    }

    for (int i = l; i <= r; i++) {
        printf("%lld ", answer[i] - answer[i - 1]);
    }

    return 0;
}

image

posted @ 2024-02-21 22:07  Vsinger_洛天依  阅读(93)  评论(4编辑  收藏  举报