3.30学习记录

今天真是颓废的一天,cf写了两个思维之后做了个线段树和深搜就做不动了。
但是无论如何,也要把这一天的成果记录一下
D. Bash and a Tough Math Puzzle
虽然是1900,但是思维含量稍低,只要能稍微熟练的用一点线段树就ok

题意:
输入一个长度为n的数组,执行m次操作
操作1:询问区间l到r内是否可以修改至多一个数让整个区间的gcd为x
操作2:修改数组的第i个位置为k

思路:由于数据范围5e5,暴力显然GG,考虑区间操作采用线段树
至于gcd的操作,只要这一段区间内有多于一个取模于x不为0的数那么就不满足条件
证明:如果所有的数都取模x为0,或者只有一个取模于x不为0,那么把一个数改成x就能让gcd为x。

ok,理论存在实践开始

#include <bits/stdc++.h>
using namespace std;
#define N 500000+100
#define ls p<<1
#define rs p<<1|1
#define mid (l+r)/2
int a[N];
int tree[N<<2];
void up(int p)
{
    tree[p]=__gcd(tree[ls],tree[rs]);
}
void bulid(int l,int r,int p)
{
    if(l==r)
    {
        tree[p]=a[l];
        return ;
    }
    bulid(l,mid,ls);
    bulid(mid+1,r,rs);
    up(p);
}
void fix(int l,int r,int pos,int k,int p)
{
    if(l==r)
    {
        tree[p]=k;
        return ;
    }
    if(pos<=mid)
    {
        fix(l,mid,pos,k,ls);
    }
    if(pos>mid)
    {
        fix(mid+1,r,pos,k,rs);
    }
    up(p);
}
int query(int l,int r,int nl,int nr,int p,int k)
{
    int res=0;
    if(l==r)
    {
        if(tree[p]%k)
            return 1;
        else
            return 0;
    }
    if(nl<=mid)
        res+=query(l,mid,nl,nr,ls,k);
    if(nr>mid)
        res+=query(mid+1,r,nl,nr,rs,k);
    return res;
}
int main()
{
    int n,q;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    bulid(1,n,1);
    cin>>q;
    for(int i=1;i<=q;i++)
    {
        int c,l,r,k;
        cin>>c;
        if(c==1)
        {
            cin>>l>>r>>k;
            if(query(1,n,l,r,1,k)>1)
            {
                cout<<"NO"<<endl;
            }
            else
            {
                cout<<"YES"<<endl;
            }
        }
        else
        {
            cin>>l>>k;
            fix(1,n,l,k,1);
        }
    }
    return 0;
}
 

然后你就会惊讶的发现:卧槽?TLE了

那我看看那里还能优化
0.头文件优化
1.输入输出优化
2.gcd函数优化
3.线段树优化
由于线段树的修改和建立操作已经无法优化了,所以考虑优化查询操作
我们发现查询操作是这样写的

int query(int l,int r,int nl,int nr,int p,int k)
{
    int res=0;
    if(l==r)
    {
        if(tree[p]%k)
            return 1;
        else
            return 0;
    }
    if(nl<=mid)
        res+=query(l,mid,nl,nr,ls,k);
    if(nr>mid)
        res+=query(mid+1,r,nl,nr,rs,k);
    return res;
}

实际上我们可以直接在下面的if中判断是否递归

优化后第二版

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
#define N 500000+100
#define ls p<<1
#define rs p<<1|1
#define int long long
#define mid ((l+r)>>1)
int a[N];
int tree[N<<2];
void up(int p)
{
    tree[p]=__gcd(tree[ls],tree[rs]);
}
void bulid(int l,int r,int p)
{
    if(l==r)
    {
        tree[p]=a[l];
        return ;
    }
    bulid(l,mid,ls);
    bulid(mid+1,r,rs);
    up(p);
}
void fix(int l,int r,int pos,int k,int p)
{
    if(l==r)
    {
        tree[p]=k;
        return ;
    }
    if(pos<=mid)
    {
        fix(l,mid,pos,k,ls);
    }
    if(pos>mid)
    {
        fix(mid+1,r,pos,k,rs);
    }
    up(p);
}
int query(int l,int r,int nl,int nr,int p,int k)
{
    int res=0;
    if(l==r)
    {
       return 1;
    }
    if(nl<=mid&&tree[ls]%k)
        res+=query(l,mid,nl,nr,ls,k);
    if(nr>mid&&tree[rs]%k)
        res+=query(mid+1,r,nl,nr,rs,k);
    return res;
}
signed main()
{
    int n,q;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    bulid(1,n,1);
    cin>>q;
    for(int i=1;i<=q;i++)
    {
        int c,l,r,k;
        scanf("%lld",&c);
        if(c==1)
        {
            scanf("%lld%lld%lld",&l,&r,&k);
            if(query(1,n,l,r,1,k)>1)
            {
                puts("NO");
            }
            else
            {
                puts("YES");
            }
        }
        else
        {
            scanf("%lld%lld",&l,&k);
            fix(1,n,l,k,1);
        }
    }
    return 0;
}

你会发现:WTF!TLE on TEST 68!?
我们发现第68组样例还是和蔼可亲的500000数据拉满,不过这次人家为了测试你是否优化查询操作到尾,特地搞了400000个查询操作(我日你先人)

我们再来看优化,发现query中记录的res值实际上到了大于1以后就不用记录了,所以我们可以加一个可行性剪枝

终于AC代码诞生了

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
#define N 500000+100
#define ls p<<1
#define rs p<<1|1
#define int long long
#define mid ((l+r)>>1)
int a[N];
int cnt;
int tree[N<<2];
void up(int p)
{
    tree[p]=__gcd(tree[ls],tree[rs]);
}
void bulid(int l,int r,int p)
{
    if(l==r)
    {
        tree[p]=a[l];
        return ;
    }
    bulid(l,mid,ls);
    bulid(mid+1,r,rs);
    up(p);
}
void fix(int l,int r,int pos,int k,int p)
{
    if(l==r)
    {
        tree[p]=k;
        return ;
    }
    if(pos<=mid)
    {
        fix(l,mid,pos,k,ls);
    }
    if(pos>mid)
    {
        fix(mid+1,r,pos,k,rs);
    }
    up(p);
}
void query(int l,int r,int nl,int nr,int p,int k)
{
    if(cnt>1)
        return;
    if(l==r)
    {
       cnt++;
       return ;
    }
    if(nl<=mid&&tree[ls]%k)
        query(l,mid,nl,nr,ls,k);
    if(nr>mid&&tree[rs]%k)
       query(mid+1,r,nl,nr,rs,k);
}
signed main()
{
    int n,q;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    bulid(1,n,1);
    cin>>q;
    for(int i=1;i<=q;i++)
    {
        int c,l,r,k;
        scanf("%lld",&c);
        if(c==1)
        {
            scanf("%lld%lld%lld",&l,&r,&k);
            cnt=0;
            query(1,n,l,r,1,k);
            if(cnt>1)
            {
                puts("NO");
            }
            else
            {
                puts("YES");
            }
        }
        else
        {
            scanf("%lld%lld",&l,&k);
            fix(1,n,l,k,1);
        }
    }
    return 0;
}
 

记录一下我和这题的相爱相杀
在这里插入图片描述

反思:
1.gcd的思维含量有待补充
2.线段树点操作有待精进
3.线段树的优化不熟悉

好了,既然提到了剪枝,我们不得不说今天做的这个题了

根据题目,我们发现这题是DP题,很像背包问题,但是今天我啊,就是想深搜过他!

题意:n个积木摞两塔,要求两个塔一样高,问问到底能多高。

思路:我们分三个方向DFS
1.第n个积木不用
2.第n个积木给第一个塔
3.第n个积木给第二个塔

我们发现,这复杂度,爆炸了。

我们加一点点剪枝

可行性剪枝

if(now==n+1)
        return;
    if(h1+sum[n]-sum[now-1]<h2)
        return;
    if(h2+sum[n]-sum[now-1]<h1)
        return;

最优解剪枝

if(h1+sum[n]-sum[now-1]<=maxx)
        return;
    if(h2+sum[n]-sum[now-1]<=maxx)
        return;
    if(h1+sum[n]-sum[now-1]==h2)
    {
         maxx=max(maxx,h2);
        return;
    }
    if(h2+sum[n]-sum[now-1]==h1)
    {
         maxx=max(maxx,h1);
        return;
    }

剪枝完了发现:最后两个n给到50的点还是T(我草这年轻人)

最后上DFS优化的最后一件法宝:记忆化

#include <bits/stdc++.h>
#define ll long long
#define N 100010
#define M 60

using namespace std;
int n, a[M], ans;
int sum[M];
map<pair<int, pair<int, int> >, int>ma;

int read() {
	int s = 0, f = 0; char ch = getchar();
	while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

bool cmp(int a, int b) {
	return a > b;
}

int dfs(int now, int h1, int h2) {
	if (now == n + 1) {
		if (h1 == h2) {
			ans = max(ans, h1);
			return h1;
		} 
		return -1;
	}
	if (ma[make_pair(now, make_pair(h1, h1))])
		return ma[make_pair(now, make_pair(h1, h1))];
	if (h1 + h2 + sum[n] - sum[now - 1] <= ans * 2) return -1;
	if (h1 + sum[n] - sum[now - 1] <= ans) return -1;
	if (h2 + sum[n] - sum[now - 1] <= ans) return -1;
	if (h1 + sum[n] - sum[now - 1] < h2) return -1;
	if (h2 + sum[n] - sum[now - 1] < h1) return -1;
	int s = 0;
	s = max(s, dfs(now + 1, h1 + a[now], h2));
	s = max(s, dfs(now + 1, h1, h2 + a[now]));
	s = max(s, dfs(now + 1, h1, h2));
	ma[make_pair(now, make_pair(h1, h1))] = s;
	return s;
}

int main() {
	n = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	sort(a + 1, a + n + 1, cmp);
	for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
	dfs(1, 0, 0);
	if (ans == 0) puts("-1");
	else cout << ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值