2021.10.20 (2)

T1:计数

Problem:

给出 m 个数 a[1],a[2],…,a[m]
求 1~n 中有多少数不是 a[1],a[2],…,a[m]的倍数。

Solution:

容斥原理即可
时间复杂度 O(2m*log)

Code:

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 ll gcd(ll a,ll b){
 5     return (!b)?a:gcd(b,a%b);
 6 }
 7 ll ans=0;
 8 int n,m,a[25];
 9 ll Gcd(ll a,ll b){
10     if(!b) return a;
11     return gcd(b,a%b);
12 }
13 void dfs(int dep,ll t,int flag){
14     if(t>n) return ;
15     if(dep==m+1){
16         ans+=n/t*flag;
17         return ;
18     }
19     dfs(dep+1,t,flag);
20     dfs(dep+1,t/Gcd(t,a[dep])*a[dep],-flag);
21 }
22 int main(){
23     scanf("%d%d",&n,&m);
24     for(int i=1;i<=m;i++) scanf("%d",&a[i]);
25     dfs(1,1,1);
26     cout<<ans<<endl;
27     return 0;
28 }

T2:区间第k大

Problem:

一个区间的价值定义为该区间中的最大值减最小值
给定 n 个数,求所有区间价值中,第 k 大值为多少。

Solution:

二分答案 ans,统计有多少个区间的价值大于等于 ans
区间[l-1,r]的价值一定大于等于[l,r]
所以对于每一右端点 i,必定存在一个阀值 Ki 使得对于所有的 l<=Ki [l,i]的价值必定大于等于ans
且随之 i 的增大,Ki 也必定单调不降
用两个单调队列来维护阀值 K 的移动(一个维护最小值,一个维护最大值,当 K 增加时判定最大值-最小值是否大于等于 ans 即可)
时间复杂度 O(log2109*n)

Code:

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int N=410000;
 5 int a[N],q1[N],q2[N];
 6 ll k;
 7 int n;
 8 bool check(int kk){
 9     int l1=1,r1=1,l2=1,r2=1;
10     q1[1]=1;
11     q2[1]=1;
12     int z=1;
13     ll ans=0;
14     if(kk==1)ans=1;
15     else ans=0;
16     for(int i=2;i<=n;i++){
17         while(l1<=r1&&a[q1[r1]]>=a[i]) --r1;
18         q1[++r1]=i;
19         while(l2<=r2&&a[q2[r2]]<=a[i]) --r2;
20         q2[++r2]=i;
21         while(z<i){
22                 int t1=l1,t2=l2;
23                 ++z;
24                 while(q1[t1]<z) ++t1;
25                 while(q2[t2]<z) ++t2;
26                 if(a[q2[t2]]-a[q1[t1]]>=kk){
27                     l1=t1;
28                     l2=t2;
29                 }
30                 else{
31                     --z;
32                     break;
33                 }
34         }
35         if(a[q2[l2]]-a[q1[l1]]>=kk) ans+=z;
36     }
37     return ans>=k;
38 }
39 int main(){
40     scanf("%d%lld",&n,&k);
41     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
42     int l=0,r=1000000000;
43     while(l<r){
44         int mid=(l+r+1)/2;
45         if(check(mid)) l=mid;
46         else r=mid-1;
47     }
48     cout<<l<<endl;
49     return 0;
50 }

T3:武器分配

Problem:

有 n 个堡垒排成一排构成了一条防御线。现在需要将 n 个武器放入这 n 个堡垒中,每个堡垒放一个,每个武器有攻击力和战场贡献值两个属性。
由于这 n 个武器都不是人为操控的,所以会对其某半径内所有单位进行攻击,而这就导致某些堡垒的互相攻击。现在发现第 i 个堡垒会和第 j 个堡垒互相攻击当且仅当|i-j|<=r,且攻击力较低的武器和他所在的堡垒会破损。
现在你需要给出一种武器分配方案使得未破损武器的战场贡献值总和最大。为了方便你只需输出战场贡献值总和的最大值即可。

Solution:

考虑如果选定了几个武器要求这些武器都不被摧毁,判断是否可能
按照攻击力从小到大把每个选定的武器放入,每放入一个选定的武器后在其后面添加 r 个比他攻击力小的武器,若当前找不出 r 个比它攻击力小的武器则判断失败

Code:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int INF=1000000000;
 4 const int N=5100;
 5 int dp[N/2][N],n,r;
 6 struct node{
 7     int x,y;
 8 }q[N];
 9 bool cmp(node a,node b){
10     return a.x<b.x;
11 }
12 void solved(){
13     scanf("%d%d",&n,&r);
14     for(int i=1;i<=n;i++) scanf("%d",&q[i].x);
15     for(int i=1;i<=n;i++) scanf("%d",&q[i].y);
16     sort(q+1,q+n+1,cmp);
17     int m=n/(1+r)+((n%(1+r))>0);
18     int ans=0;
19     for(int i=1;i<=m;i++){
20         int tt=min((1+r)*i,n);
21         for(int j=0;j<=tt-1;j++) dp[i][j]=-INF;
22         for(int j=tt;j<=n;j++) dp[i][j]=q[j].y+dp[i-1][j-1];
23         for(int j=1;j<=n;j++) dp[i][j]=max(dp[i][j],dp[i][j-1]);
24         ans=max(ans,dp[i][n]);
25     }
26     printf("%d\n",ans);
27 }
28 int main(){
29     int T;
30     scanf("%d",&T);
31     while(T--) solved();
32     return 0;
33 }

 

posted @ 2021-10-20 20:01  B_lank  阅读(39)  评论(0编辑  收藏  举报