B小红不想做鸽巢原理

思路:最后剩下的小球个数一定是sum%k个,那么只需从最多的小球开始取,直到取够这么多为止,时间复杂度O(nlogn)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),k=sc.nextInt(),sum=0,a[]=new int[n];
        for(int i=0;i<n;i++){
            a[i]=sc.nextInt();
            sum=(sum+a[i])%k;
        }
        if(sum==0){
            System.out.println("0");
            return;
        }
        //取完最后一次,剩下sum个
        Arrays.sort(a);
        for(int i=n-1;i>=0;i--){
            if(sum-a[i]<=0){
                System.out.println(n-i);
                return;
            }
            sum-=a[i];
        }
    }
}

C小红不想做完全背包(easy) and D小红不想做完全背包 (hard)

题目要求最少取一个球,取得的球只跟其价值mod p有关,首先特判球中有p倍数的,直接返回1;;其次把取得每种余数的球看做一个状态,每个球可以在两种状态之间连边,之后跑一遍最短路,完全图遍历的时间复杂度最大为O(p^2)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),p=sc.nextInt();
        Set<Integer> path[]=new Set[p];
        for(int i=0;i<p;i++){
            path[i]=new HashSet<>();
        }
        for(int i=0;i<n;i++){
            int a=sc.nextInt()%p;
            if(a==0){
                System.out.println("1");
                return;
            }
            for(int j=0;j<p;j++){
                path[j].add((j+a)%p);
            }
        }
        int dis[]=new int[p],ans=p+5;
        Arrays.fill(dis,p+5);
        dis[0]=0;
        Queue<Integer> q=new PriorityQueue<>();
        q.add(0);
        while(!q.isEmpty()){
            int a=q.poll();
            for(int b:path[a]){
                if(dis[b]>dis[a]+1){
                    q.add(b);
                    dis[b]=dis[a]+1;
                }
            }
        }
        for(int i=1;i<p;i++){
            ans=Math.min(ans,dis[i]+dis[p-i]);
        }
        System.out.println(ans);
    }
}

E小红不想做莫比乌斯反演杜教筛求因子和的前缀和

思路:枚举长宽即可,可推出高是否存在以及数据范围是否合格,时间复杂度O(mn)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),m=sc.nextInt(),p=sc.nextInt(),x=sc.nextInt(),ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                int d=x-i*j;
                if(d>0&&d%((i+j)*2)==0&&d/(i+j)/2<=p){
                    ans++;
                }
            }
        }
        System.out.println(ans);
    }
}

F小红不想做模拟题

线段树,节点内分别懒标记对应字符串时时的1的个数,以及两者都为1的个数,修改操作可以看做把一个串的1的个数改为区间长度,共同个数改为另一个串的1的个数,时间复杂度O(n+qlogn)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        char a[]=sc.next().toCharArray(),b[]=sc.next().toCharArray();
        SegTree st=new SegTree(0,n-1);
        build(st,a,b);
        int q=sc.nextInt();
        for(int i=0;i<q;i++){
            String op=sc.next();
            int l=sc.nextInt()-1,r=sc.nextInt()-1;
            modify(st,l,r,op);
            System.out.println(st.count);
        }
    }
    static void clear(SegTree st){
        int l=st.l,r=st.r;
        if(l<r){
            int mid=(l+r)>>1;
            if(st.changed1){
                st.changed1=false;
                st.left.num1=mid-l+1;
                st.right.num1=r-mid;
                st.left.count=st.left.num2;
                st.right.count=st.right.num2;
                st.left.changed1=st.right.changed1=true;
            }
            if(st.changed2){
                st.changed2=false;
                st.left.num2=mid-l+1;
                st.right.num2=r-mid;
                st.left.count=st.left.num1;
                st.right.count=st.right.num1;
                st.left.changed2=st.right.changed2=true;
            }
            st.count=st.left.count+st.right.count;
            st.num1=st.left.num1+st.right.num1;
            st.num2=st.left.num2+st.right.num2;
        }
    }
    static void modify(SegTree st,int a,int b,String op){
        int l=st.l,r=st.r;
        clear(st);
        if(l==a&&r==b){
            if(op.equals("A")){
                st.num1=r-l+1;
                st.count=st.num2;
                st.changed1=true;
            }
            else{
                st.num2=r-l+1;
                st.count=st.num1;
                st.changed2=true;
            }
        }
        else{
            int mid=(l+r)>>1;
            if(b<=mid){
                modify(st.left,a,b,op);
            }
            else if(a>mid){
                modify(st.right,a,b,op);
            }
            else{
                modify(st.left,a,mid,op);
                modify(st.right,mid+1,b,op);
            }
            st.count=st.left.count+st.right.count;
            st.num1=st.left.num1+st.right.num1;
            st.num2=st.left.num2+st.right.num2;
        }
    }
    static void build(SegTree st,char c1[],char c2[]){
        int l=st.l,r=st.r;
        if(l==r){
            st.count=c1[l]==c2[l]&&c1[l]=='1'?1:0;
            st.num1=c1[l]-'0';
            st.num2=c2[l]-'0';
        }
        else{
            int mid=(l+r)>>1;
            st.left=new SegTree(l,mid);
            st.right=new SegTree(mid+1,r);
            build(st.left,c1,c2);
            build(st.right,c1,c2);
            st.count=st.left.count+st.right.count;
            st.num1=st.left.num1+st.right.num1;
            st.num2=st.left.num2+st.right.num2;
        }
    }
}
class SegTree{
    SegTree left,right;
    int l,r,count,num1,num2;
    //count为同时为1的个数,num12为ab中1的个数
    boolean changed1=false,changed2=false;
    public SegTree(int l,int r){
        this.l=l;
        this.r=r;
    }
}

G小红不想做平衡树

用有序集合分别存储下波峰和波谷的坐标。,,好子区间的一定要么是单增,单减、增减、减增、增减增这五种,根据起始坐标在(增区间或者是减区间)的位置分类计数即可,时间复杂度O(nlogn)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),a[]=new int[n];
        for(int i=0;i<n;i++){
            a[i]=sc.nextInt();
        }
        TreeSet<Integer> peak=new TreeSet<>(),val=new TreeSet<>();
        for(int i=0;i<n;i++){
            if((i==0||a[i]>a[i-1])&&(i==n-1||a[i]>a[i+1])){
                peak.add(i);
            }
            if((i==0||a[i]<a[i-1])&&(i==n-1||a[i]<a[i+1])){
                val.add(i);
            }
        }
        long ans=1;
        for(int i=0;i<n-1;i++){
            if(atDec(i,peak,val)){
                //可以是先下降,这一段都是好区间
                //后边转折的第一个数字,值要比i处的大才有必要继续找下去
                Integer nextVal=val.ceiling(i);
                ans+=nextVal-i+1;
                if(nextVal<n-1&&a[nextVal+1]>a[i]){
                    Integer nextPeak=peak.ceiling(nextVal);
                    ans+=nextPeak-nextVal;
                }
            }
            else{
                //先找到第一个peak
                Integer nextPeak=peak.ceiling(i);
                ans+=nextPeak-i+1;
                if(nextPeak<n-1){
                    //此时下一个山峰并不是最后一个,那么需要找到下一个山谷
                    Integer nextVal=val.ceiling(nextPeak);
                    if(a[nextVal]<a[nextPeak-1]){
                        int l=nextPeak,r=nextVal;
                        while(l<r){
                            int mid=(l+r)>>1;
                            if(a[mid]>a[nextPeak-1]){
                                l=mid;
                            }
                            else{
                                r=mid-1;
                            }
                            if(l==r-1){
                                if(a[r]>a[nextPeak-1]){
                                    l=r;
                                }
                                break;
                            }
                        }
                        ans+=l-nextPeak;
                    }
                    else{
                        ans+=nextVal-nextPeak;
                        if(nextVal<n-1&&a[nextVal+1]>a[nextPeak]){
                            nextPeak=peak.ceiling(nextVal);
                            ans+=nextPeak-nextVal;
                        }
                    }
                }
            }
        }
        System.out.println(ans);
    }
    static boolean atDec(int idx,TreeSet<Integer> peak,TreeSet<Integer> val){
        //判断idx是是否在下降区间
        if(peak.contains(idx)){
            return true;
        }
        if(val.contains(idx)){
            return false;
        }
        Integer nextVal=val.ceiling(idx),nextPeak=peak.ceiling(idx);
        return nextVal!=null&&(nextPeak==null||nextPeak>nextVal);
    }
}