• 【LGR-049】洛谷7月月赛


    Preface

    Luogu八月月赛都结束了我才来补七月月赛

    这次月赛还是很狗的,在绍一的晚上恰逢刮台风,然后直接打到一半断网了

    结果都没有交上去GG

    感觉这次难度适中,解法也比较清新自然吧,十分给个九分一分因为没的打

    好了下面开始看题。


    A Divided Prime

    目的:送分,坑罚时

    一道比较SB的题目,尤其注意题目中一个信息:

    保证对于一个数字,其在(b_i)中出现的次数不多于在(a_i)中出现的次数。

    然后我们发现这个式子的本质就是一些数的乘积,我们讨论一下最后的结果:

    • 由两个及以上的数相乘得来,绝对不是质数
    • 是一个数的形式,(O(sqrt n))判断是质数还是合数

    当然你不能傻乎乎地直接把一堆数乘起来,你又不是Python

    我们离散化一下,然后用类似于桶一样的操作即可实现消去

    CODE

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long LL;
    const LL N=100005;
    LL a[N],b[N],r[N<<1],t1[N<<1],t2[N<<1],tot,n,m,t,cnt,num;
    bool flag;
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(LL &x)
    {
    	x=0; char ch; while (!isdigit(ch=tc()));
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void clear(void)
    {
    	memset(t1,0,sizeof(t1));
    	memset(t2,0,sizeof(t2)); 
    	cnt=tot=num=0; flag=1;
    }
    inline LL find(LL x)
    {
    	LL L=1,R=cnt;
    	while (L<=R)
    	{
    		LL mid=(L+R)/2;
    		if (r[mid]==x) return mid;
    		if (r[mid]>x) R=mid-1; else L=mid+1;
    	}
    }
    inline bool is_prime(LL x)
    {
    	if (x==1) return 0;
    	for (register int i=2;i*i<=x;++i)
    	if (x%i==0) return 0; return 1;
    }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	register LL i; read(t);
    	while (t--)
    	{
    		clear(); read(n); read(m);
    		for (i=1;i<=n;++i) read(a[i]),r[++cnt]=a[i];
    		for (i=1;i<=m;++i) read(b[i]),r[++cnt]=b[i];
    		sort(r+1,r+cnt+1); cnt=unique(r+1,r+cnt+1)-r-1;
    		for (i=1;i<=n;++i) ++t1[find(a[i])];
    		for (i=1;i<=m;++i) ++t2[find(b[i])];
    		for (i=1;i<=cnt;++i)
    		if (t1[i]-t2[i]==1)
    		{
    			if (!num) num=r[i]; else { flag=0; break; }
    		} else if (t1[i]!=t2[i]) { flag=0; break; }
    		if (!flag||!num) puts("NO"); else puts(is_prime(num)?"YES":"NO");
    	}
    	return 0;
    }
    

    B River Jumping

    目的:送分,连罚时都骗不到

    话说这题问什么蓝了,我觉得黄题都过了

    看到题目和陈潇然大佬讨论了一波觉得直接贪心地跳,每次选择一个最近的且大于(S)的跳一下即可。

    具体的证明感觉真的不需要

    对于一段岩石(iin[l,r]),若(w_r-w_l<S),由于来回一次的限制,中间的石块便无法被跳完。

    因此若有解,我们只需要按上面的方法做即可。

    感觉没什么坑点,5min码完1A当然是赛后

    CODE

    #include<cstdio>
    #include<cctype>
    using namespace std;
    const int N=100005;
    int n,m,s,lst,cnt,ans[N],pre[N],dis,tot,x;
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; while (!isdigit(ch=tc()));
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
    	if (x>9) write(x/10);
    	putchar(x%10+'0');
    }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	register int i; read(n); read(m); read(s);
    	for (pre[1]=0,tot=1,i=1;i<=m;++i)
    	{
    		read(x); if (x-lst>=s) ans[++cnt]=i,lst=x;
    		else if (x-dis<s) return puts("NO"),0; else pre[++tot]=i,dis=x;
    	}
    	if (n-lst>=s) ans[++cnt]=i; else return puts("NO"),0;
    	for (puts("YES"),i=1;i<=cnt;++i) write(ans[i]),putchar(' ');
    	for (i=tot;i>=1;--i) write(pre[i]),putchar(' ');
    	return 0;
    }
    

    C True Vegetable

    目的:拉分题,考验脑洞和思维

    由于数据范围我们想到二分,但不是常规的二分时间,因为这样我们很难直接判断

    考虑二分被减的时间点。接下来我们先把题目的菜气减去被打掉的值。

    这里忍不住又%一发出题人,把一个细节的数据(w_i+r_{v_i}le w_{i+1})藏的这么深

    所以我们发现菜气被打掉之后总是可以涨回来的,于是直接贪心大力加即可。

    考虑当前的这个点不满足,那么尽量向右选择即可。

    CODE

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    using namespace std;
    const int N=5e5+5;
    int n,m,k,s,a[N],x,w[N],num[N],v[N],t[N],ans,d[N];
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch; while (!isdigit(ch=tc()));
        while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline int work(int x)
    {
        register int i; int tot=0,step=0;
        memcpy(t,a,sizeof(t));
        memset(d,0,sizeof(d));
        for (i=1;i<=x;++i)
        t[num[i]]-=v[i];
        for (i=1;i<=n;++i)
        {
            if (t[i]+tot<1)
            {
                int dlt=1-t[i]-tot; step+=dlt; tot+=dlt;
                if (d[i+k-1]<=n) d[i+k-1]+=dlt;
            }
            tot-=d[i];
        }
        return step>w[x]?step:w[x];
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i; read(n); read(m); read(k); read(s);
        for (i=1;i<=n;++i) read(a[i]);
        for (i=1;i<=s;++i) read(x);
        for (i=1;i<=m;++i)
        read(w[i]),read(num[i]),read(v[i]);
        int l=0,r=m,mid; w[m+1]=1e9;
        while (l<=r)
        {
            mid=l+r>>1;
            if (work(mid)<w[mid+1]) ans=mid,r=mid-1; else l=mid+1;
        }
        return printf("%d",work(ans)),0;
    }
    

    D Beautiful Pair

    目的:毒瘤数据结构,打压选手的信心

    看到题目发现(O(n^2))的暴力是naive的,考虑优化。

    由于最近做过类似的题目,我就直接考虑当(a_i)为一个区间(iin[l,r])中的最大值时,对答案的贡献

    首先两边是可以直接用单调栈(O(n))搞出来的。

    借鉴一下启发式的思想,我们每一次以(i)为中心把区间分成左右两边,并挑选数字个数少的一边直接枚举

    同时在另一边的记录询问(注意这里的询问相当与求小于某一个数的数的数量)这个可以树状数组存一下

    考虑我们把所有的操作都放到最后,渐进意义下期望的时间和空间复杂度都是控制在(O(n logn))的范围内的(好像有大佬能证明更少)

    所以最后扫一次在树状数组上跑并且统计即可,注意区间计算之后要及时删除

    复杂度是很迷的(O(n log^2n)),结果不吸氧也能过

    CODE

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<vector>
    #define pb push_back
    using namespace std;
    const int N=100005;
    int n,a[N],b[N],stack[N],top,front[N],back[N],num[N],tot,bit[N];
    vector <int> d[N];
    long long ans;
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch; while (!isdigit(ch=tc()));
        while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void insert(int now,int opt)
    {
        if (opt)
        {
            d[now].pb(1); d[front[now]-1].pb(-1);
            for (register int i=now+1;i<=back[now];++i)
            d[now].pb(a[now]/a[i]),d[front[now]-1].pb(-a[now]/a[i]);
        } else
        {
            d[back[now]].pb(1); d[now-1].pb(-1);
            for (register int i=front[now];i<now;++i)
            d[back[now]].pb(a[now]/a[i]),d[now-1].pb(-a[now]/a[i]);
        }
    }
    inline int abs(int x)
    {
        return x>0?x:-x;
    }
    inline int lowbit(int x)
    {
        return x&-x;
    }
    inline void add(int x)
    {
        while (x<=n) ++bit[x],x+=lowbit(x);
    }
    inline int get(int x)
    {
        int tot=0; while (x) tot+=bit[x],x-=lowbit(x); return tot;
    }
    inline int same_find(int x)
    {
        int l=1,r=tot;
        while (l<=r)
        {
            int mid=l+r>>1; if (b[mid]==x) return mid;
            if (b[mid]<x) l=mid+1; else r=mid-1;
        }
    }
    inline int lower_find(int x)
    {
        int l=1,r=tot,res;
        while (l<=r)
        {
            int mid=l+r>>1;
            if (b[mid]<=x) res=mid,l=mid+1; else r=mid-1;
        }
        return res;
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i,j; read(n);
        for (i=1;i<=n;++i)
        read(a[i]),b[i]=a[i];
        for (i=1;i<=n;++i)
        {
            while (top&&stack[top]<=a[i]) --top;
            front[i]=top?num[top]+1:1;
            stack[++top]=a[i]; num[top]=i;
        }
        for (top=0,i=n;i>=1;--i)
        {
            while (top&&stack[top]<a[i]) --top;
            back[i]=top?num[top]-1:n;
            stack[++top]=a[i]; num[top]=i;
        }
        for (i=1;i<=n;++i)
        if (i-front[i]<=back[i]-i) insert(i,0); else insert(i,1);
        sort(b+1,b+n+1); tot=unique(b+1,b+n+1)-b-1;
        for (i=1;i<=n;++i)
        {
            add(same_find(a[i]));
            for (j=0;j<d[i].size();++j)
            {
                int s=lower_find(abs(d[i][j]));
                ans+=d[i][j]>0?get(s):-get(s);
            }
        }
        return printf("%lld",ans),0;
    }
    

    E题凸包弃疗,感觉做不动

    总体还可以吧,挺喜欢这次月赛的

    希望下次不要断网了

  • 相关阅读:
    06-继承与多态(动手动脑与验证)
    课堂测试四(文件读写)
    字符串加密
    String方法阅读笔记
    03-类与对象(动手动脑)
    计算已经创建了多少个对象
    递归练习(课程作业)
    02-方法(动手动脑)
    猜数字游戏
    JAVA语言实现简单登录界面
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9434042.html
Copyright © 2020-2023  润新知