4.12学习记录

D. Bash and a Tough Math Puzzle

gcd线段树

注意点:询问剪枝

#include <bits/stdc++.h>
using namespace std;
const int N = 5e6+10000;
#define int long long
#define ls p<<1
#define rs p<<1|1
#define mid (l+r)/2
int con=0;
int tree[N<<2],a[N];
void up(int p)
{
    tree[p]=std::__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(mid>=pos)
    {
        fix(l,mid,pos,k,ls);
    }
    if(mid<pos)
    {
        fix(mid+1,r,pos,k,rs);
    }
    up(p);
}
void query(int l,int r,int nl,int nr,int p,int k)
{
    if(con>1)
        return ;
    if(l==r)
    {
        con++;
        return ;
    }
    if(mid>=nl&&tree[ls]%k)
    {
        query(l,mid,nl,nr,ls,k);
    }
    if(mid<nr&&tree[rs]%k)
    {
        query(mid+1,r,nl,nr,rs,k);
    }
}
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    bulid(1,n,1);
    int m;
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        int c;
        cin>>c;
        if(c==1)
        {
            con=0;
            int l,r,k;
            cin>>l>>r>>k;
            query(1,n,l,r,1,k);
 
            if(con>=2)
            {
                cout<<"NO"<<endl;
            }
            else
            {
                cout<<"YES"<<endl;
            }
        }
        else
        {
             int pos,k;
             cin>>pos>>k;
             fix(1,n,pos,k,1);
        }
    }
    return 0;
}
/*
gcd,如果有大于1个取模于k不为0,那么必然是符合方案的
 
*/

C. Tree Infection

二分答案

这题妙
我的思路:二分出感染次数然后贪心,

最少感染次数就是所有有子节点的节点数+1,然后用一个数组b记录每个节点的儿子们在感染次数执行过程中最多能有多少个被感染,如果原本的儿子数大于b数组中的数目,那么用sum加上差值,如果sum+最少感染次数要大于二分答案,那么就返回false,否则返回true

#include <bits/stdc++.h>
using namespace std;
const int N = 2e6+10000;
int rd[N];
int b[N];
int n;
bool cmp(int a,int b)
{
    return a>b;
}
bool check(int mid)
{
    for(int i=1;i<=n;i++)
        b[i]=0;
    int cnt=0;
    for(int i=1;i<=mid;i++)
    {
        if(rd[i])
        {
            b[i]=mid-cnt;
            cnt++;
        }
    }
    if(cnt>mid)
    {
        return false;
    }
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        if(rd[i]&&rd[i]>b[i])
        {
            sum+=rd[i]-b[i];
        }
    }
    if(cnt+sum>mid)
        return false;
    return true;
}
int main()
{
   
    int t;
    for(cin>>t;t;t--)
    {
        cin>>n;
        for(int i=1;i<=n+1;i++)
            rd[i]=0;
        rd[1]++;
        for(int i=1;i<n;i++)
        {
            int temp;
            cin>>temp;
            rd[temp+1]++;
        }
        n++;
        sort(rd+1,rd+n+1,cmp);
        int l=0,r=n;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(check(mid))
            {
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        cout<<l<<endl;
    }
    return 0;
}

C. Good Subarrays

前缀和

发现如果区间长度为区间和就是符合题意的情况

sum[i]-sum[j-1]=i-j+1就是符合的
移项后
sum[i]-i=sum[j-1]-j+1
不难看出问题转换为了统计sum[i]-i出现的次数

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ls p<<1
#define rs p<<1|1
#define mid (l+r)/2
const int mod=1e9+7;
const int N = 1e6+100;
int a[N];
int sum[N];
int pre[N];
signed main()
{
    int t;
    for(cin>>t;t;t--)
    {
        memset(pre,0,sizeof pre);
        int n;
        cin>>n;
        char ch;
        int ans=0;
        pre[100000]++;
        for(int i=1;i<=n;i++)
        {
            cin>>ch;
            a[i]=ch-'0';
            sum[i]=sum[i-1]+a[i];
            ans+=pre[sum[i]-i+100000];
            pre[sum[i]-i+100000]++;
        }
        cout<<ans<<endl;
        for(int i=1;i<=n;i++)
        {
            a[i]=sum[i]=0;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值