10.14 模拟赛

T1

思路

容斥原理

主要是处理3 4 5....个数字的lcm时出了问题

由于一开始容斥原理用错了,求的是乘积(乘积只针对互质的数,而此题数据显然没有限制)

对拍发现错误样例后,在a[i]中删除了一个数是另一个数的整倍数的情况

然后就为下文怎么改也改不出来做了铺垫

正确的解法应该是俩俩求lcm

a:6 9 5 7

6 9 5lcm

lcm((6,9),5)=lcm(18,5)=90

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m;
int a[25];
bool chosen[25];
long long tot[25];
inline int findgcd(int x,int y){
    return !y?x:findgcd(y,x%y);
}
//now:已选个数,x:总共选择个数,mul:目前乘积 
void work(int now,int x,long long mul,int maxx){
    if(mul>(long long)n) return;
    if(now==x){
        tot[x]+=n/mul;
        return;
    }
    for(int i=maxx+1;i<=m;i++){
        if(!chosen[i]){
            int gcd=findgcd(mul,a[i]);
            long long ret=mul/gcd*a[i];
            chosen[i]=true;
            work(now+1,x,ret,i);
            chosen[i]=false;
        }
    }
}
int main(){
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&a[i]);
    long long answer=0;
    for(int i=1;i<=m;i++){
        work(0,i,1,0);
    }
    for(int i=1;i<=m;i++){
        if(i%2) answer+=tot[i];
        else answer-=tot[i];
    }
    answer=n-answer;
    cout<<answer<<endl;
    return 0;
}

T2

思路

二分第k大值

Check(int kk):判断当前价值比mid大的区间是不是比k个多

“对于每个固定的右端点,它左侧一定存在一个阀值ki使l<=ki[l,r]的价值必定大于等于ans

且随i增大,ki单调不降”

摘自某题解,我确实想不到比这更准确又更简洁的描述方式了

通俗讲,就是一旦右端点i固定了,在它左侧就会存在一个点x,使得同样以i为右端点、比[x,i]长的区间的价值比[x,i]大,而且,i变大,ki不会变小,要么不变,要么跟着变大

关于单调队列应用:

q1[i]a[i]后面第一个比a[i]大的数的位置

q2[i]a[i]后面第一个比a[i]小的数的位置

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 400005
long long a[N],q1[N],q2[N];
long long k;
long long n;
void read(long long &now){
    now=0;bool flag=false;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')flag=true;
        c=getchar();
    }
    while(c>='0'&&c<= '9'){
        now=now*10+c-'0';
        c=getchar();
    }
    now=flag?-now:now;
}
bool check(int kk){
    int l1=1,r1=1,l2=1,r2=1;
    q1[1]=1;q2[1]=1;
    int z=1;
    long long ans=0;
    if(kk==1)ans=1;
    else ans=0;
    for(int i=2;i<=n;i++){
        while(l1<=r1 && a[q1[r1]]>=a[i]) r1--;
        q1[++r1]=i;
        while(l2<=r2 && a[q2[r2]]<=a[i]) r2--;
        q2[++r2]=i;
        while(z<i){
            int t1=l1,t2=l2;z++;
            while(q1[t1]<z) t1++;
            while(q2[t2]<z) t2++;
            if(a[q2[t2]]-a[q1[t1]]>=kk){
                l1=t1;l2=t2;
            }else{
                Z--;break;
            }
        }
        if(a[q2[l2]]-a[q1[l1]]>=kk) ans+=z;
    }
    return ans>=k;
}
int main(){
    freopen("kth.in","r",stdin);
    freopen("kth.out","w",stdout);
    read(n);read(k);
    for(int i=1;i<=n;i++)read(a[i]);
    int l=0,r=1000000000;
    while(l<r){
        int mid=(l+r+1)/2;
        if(check(mid))l=mid;
        else r=mid-1;
    }
    cout<<l<<endl;
    return 0;
}

T3

思路

当时,写是最暴力的暴力

正解:

选定几个武器使其不被摧毁,对是否可能进行判断:

按照攻击力从小到大排序,要保证其不被摧毁

则后面的r个都是比它攻击力小的

如果找不够,那就不可能

所以,总结判断方法:

攻击力第i小的选定武器,其攻击力在所有武器中排名需>=i*(r+1)

按照攻击力从小到大将武器排序

dp[i][j]:j个武器选了i个且可能成立的最大贡献

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 5005
#define INF 1000000000
using namespace std;
int dp[N/2][N],n,r;
struct node{
    int x,y;
    friend bool operator < (node a,node b){
        return a.x<b.x;
    }
}q[N];
int main(){
    freopen("submax.in","r",stdin);
    freopen("submax.out","w",stdout);
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&r);
        for(int i=1;i<=n;i++) scanf("%d",&q[i].x);
        for(int i=1;i<=n;i++) scanf("%d",&q[i].y);
        sort(q+1,q+n+1);
        int m=n/(1+r)+((n%(1+r))>0);
        int ans=0;
        for(int i=1;i<=m;i++){
            int tt=min((1+r)*i,n);
            for(int j=0;j<tt;j++) dp[i][j]=-INF;
            for(int j=tt;j<=n;j++)
                dp[i][j]=q[j].y+dp[i-1][j-1];
            for(int j=1;j<=n;j++)
                dp[i][j]=max(dp[i][j],dp[i][j-1]);
            ans=max(ans,dp[i][n]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

猜你喜欢

转载自www.cnblogs.com/aptx--4869/p/9788101.html