D1. Divan and Kostomuksha (easy version) 2100 (dp 数论)
https://codeforces.com/problemset/problem/1614/D1
题解:本题应使用dp,观察每一种解的共同点,有开始点和最终gcd值两种可枚举状态,然而开始点并不好转移,故我们考虑以最终gcd值作为状态进行dp。用f[i]表示最终gcd为i时所能得到的最大值,初始化f[i]=cnt[i]。从N到1计算所有i的倍数个数,并用i的倍数进行转移,f[i]=max(f[i],f[j]+i*(c[i]-c[j])),最终所有c[i]=n的i可作为答案,枚举最大值即可。
#include<bits/stdc++.h>
#define int long long
#define fr(i,n) for(int i=1;i<=n;i++)
using namespace std;
const int N=5e6+100;
int c[N],cc[N],a[N],f[N];
signed main(){
int n,maxx=0;cin>>n;
fr(i,n){
cin>>a[i];
cc[a[i]]++;
maxx=max(a[i],maxx);
}
for(int i=maxx;i>=1;i--){
for(int j=i;j<=maxx;j+=i){
c[i]+=cc[j];
}
}
for(int i=1;i<=maxx;i++){
f[i]=c[i]*i;
}
for(int i=maxx;i>=1;i--){
for(int j=i;j<=maxx;j+=i){
f[i]=max(f[i],f[j]+i*(c[i]-c[j]));
}
}
int ans=0;
for(int i=1;i<=maxx;i++){
if(c[i]==n) ans=max(ans,f[i]);
}
cout<<ans;
}
D. Progressions Covering (线段树 贪心)
https://codeforces.com/contest/1661/problem/D
题解:我们从右向左看,最右端的点必须要满足条件,故需至少进行 \(\lceil (b[i]-a[i])/k \rceil\),次操作,显然当满足条件后将区间右端点向右移到第一个不满足条件的点上最优,进行到最后即可。维护区间上每一点的值我们可以在a的差分数组上建一颗线段树来维护每次操作后的值,复杂度O(nlogn)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10;
struct node{
int l,r,w;
}t[N*4];
int lz[N*4],a[N];
void build(int p,int l,int r){
t[p].l=l,t[p].r=r;
if(t[p].l==t[p].r){
t[p].w=a[l];return;
}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].w=(t[p*2].w+t[p*2+1].w);
}
void spread(int p){
if(!lz[p]) return;
t[p*2].w+=lz[p]*(t[p*2].r-t[p*2].l+1);
t[p*2+1].w+=lz[p]*(t[p*2+1].r-t[p*2+1].l+1);
lz[p*2]+=lz[p];
lz[p*2+1]+=lz[p];
lz[p]=0;
}
void add(int p,int l,int r,int k){
if(l<=t[p].l&&t[p].r<=r){
t[p].w+=k*(t[p].r-t[p].l+1);
lz[p]+=k;
return;
}
spread(p);
int mid=(t[p].l+t[p].r)/2;
if(l<=mid) add(2*p,l,r,k);
if(r>mid) add(2*p+1,l,r,k);
t[p].w=t[p*2].w+t[p*2+1].w;
}
int ask(int p,int l,int r){
if(t[p].l>=l&&t[p].r<=r){
return t[p].w;
}
spread(p);
int mid=(t[p].l+t[p].r)/2,res=0;
if(l<=mid) res+=ask(p*2,l,r);
if(r>mid) res+=ask(p*2+1,l,r);
return res;
}
int b[N];
signed main(){
int n,k,ans=0;cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>b[i];
}
build(1,1,n);
for(int i=n;i>=k;i--){
int w=ask(1,1,i);
w=b[i]-w;
int r=(w+k-1)/k;
if(r<=0) continue;
add(1,i-k+1,i,r);
if(i!=n)
add(1,i+1,i+1,-r*k);
ans+=r;
}
int res=0;
for(int i=1;i<k;i++){
int w=ask(1,1,i);
w=w-b[i];
if(w<0){
int r=(-w+i-1)/i;
res=max(r,res);
}
}
ans+=res;
cout<<ans;
}
D. Balancing Weapons (双指针 思维)
https://codeforces.com/contest/1814/problem/D
题解:最坏情况下我们能保证改变所有枪能够满足条件。我们考虑至少有1个gun不变,那么其他gun变换后的范围都应落在[fidi-k,fidi+k],范围内,而对于每把gun j,只需要放至多3个值在区间内即可,分别为 \(\lfloor fi*di/fj *fj \rfloor\) ,\(\lceil fi*di/fj *fj \rceil\) ,fjdj,而当dj值不用改变时其贡献1,故我们可以在区间每个点处维护数对(i,0/1),如果[l,l+k]满足所有i均在上有数即可用于更新答案。复杂度为O(n(n+k))。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=7100;
int b[N],a[N],v[N];
vector<pair<int,int> > g[N];
void solve(){
int n,k;cin>>n>>k;
int ans=0;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++){
int l=max((int)1,a[i]*b[i]-k),r=a[i]*b[i]+k;
for(int j=l-l;j<=r-l;j++) g[j].clear();
for(int j=1;j<=n;j++) v[j]=0;
int w=a[i]*b[i];
for(int j=1;j<=n;j++){
if(j==i) continue;
int u=w/a[j],ok=0;
if(u*a[j]>=l){
if(u==b[j]) ok=1,g[u*a[j]-l].push_back({j,1});
else g[u*a[j]-l].push_back({j,0});
}
u=u+1;
if(u*a[j]<=r){
if(u==b[j]) ok=1,g[u*a[j]-l].push_back({j,1});
else g[u*a[j]-l].push_back({j,0});
}
u=b[j];
if(!ok&&a[j]*u<=r&&a[j]*u>=l){
g[u*a[j]-l].push_back({j,1});
}
}
int cnt=1,re=n-1;
for(int j=l-l;j<=k;j++){
for(auto it:g[j]){
int p=it.first,h=it.second;
if(!v[p]) re--;
v[p]++;cnt+=h;
}
}
if(!re) ans=max(ans,cnt);
for(int j=l-l+1;j<=r-l-k;j++){
for(auto it:g[j-1]){
int p=it.first,h=it.second;
v[p]--,cnt-=h;
if(!v[p]) re++;
}
for(auto it:g[j+k]){
int p=it.first,h=it.second;
if(!v[p]) re--;
v[p]++;cnt+=h;
}
if(!re) ans=max(ans,cnt);
}
}
cout<<n-ans<<endl;
}
signed main(){
int T;cin>>T;
while(T--)
solve();
}
标签:总结,daliy,int,void,long,--,lz,define
From: https://www.cnblogs.com/wjhqwq/p/17300947.html