开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 27 天,点击查看活动详情
Problem - 1633D - Codeforces
先求出1转化为每一个数需要的最小次数f[b[i]],问题就转化为容量为k,每件物品的体积为f[b[i]],价值为c[i],01背包,做这题的时候脑子抽了以为只有因数才能转移,但是显然不是,比如86也可以去加9,因为86/9=9,,,做题还是要仔细一点
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int N = 3e5+10;
int t,n,k,b[1005],c[1005],f[1005],dp[1000006],w[1005],y[1005];
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
for(int i=1;i<=1000;i++) f[i]=1e18;
f[1]=0;
int maxx=0;
map<int,int>mp;
for(int i=1;i<=1000;i++)
{
for(int j=1;j<=i;j++){
int x=i/j;
if(i+x<=1000) f[i+x]=min(f[i+x],f[i]+1);
}
}
cin>>t;
while(t--)
{
cin>>n>>k;
k=min(12000LL,k);
for(int i=0;i<=k;i++) dp[i]=0;
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++) cin>>c[i];
for(int i=1;i<=n;i++)
{
for(int j=k;j>=f[b[i]];j--)
dp[j]=max(dp[j],dp[j-f[b[i]]]+c[i]);
}
cout<<dp[k]<<endl;
}
system("pause");
return 0;
}
C - Klee in Solitary Confinement
考虑每一个数x和x+k,对于x+k来说,一段区间里含的x+k越少越好,x越多越好,那么这个区间就可以都加上k;tx[x]是x在序列中的出现次数,cnt[x]是在指定的操作区间内x可以增加的个数,可以想一下假定是lr这个区间,那么遇到x我们就让cnt[x+k]++,说明我们可以让这个数加上k,然后cnt[x]=max(cnt[x],0),这句话是对于x-k和x来说的,因为这个lr区间内都加上了k,那么(x-k)+k=x,但是x+k就会让x的出现次数减少了,也就是增量会-1,但是增量最少也会是0,不会比原先更少,所以要和0比较一下,总之就是模拟实现了一下l,r这个区间内x-k的个数为a,x的个数为b,那么x可以增加的个数就是max(a-b,0);这样就可以线性的去遍历去求最大值了
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int N = 4e6+10;
const int B=2e6;
int n,k,a[N],tx[N],cnt[N];
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin>>n>>k;
int ans=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];a[i]+=B;
tx[a[i]]++;
ans=max(ans,tx[a[i]]);
}
for(int i=1;i<=n;i++)
{
cnt[a[i]+k]++;
cnt[a[i]]=max(0LL,cnt[a[i]]-1);
ans=max(ans,tx[a[i]+k]+cnt[a[i]+k]);
}
cout<<ans<<endl;
system("pause");
return 0;
}
H - Crystalfly 树形dp
f[i]表示选了i节点然后继续去走i的子节点可以收获的最大值,g[i]表示没选i节点然后继续去走i的子节点可以收获的最大值,h[i]表示选了i节点但是没有选i的子节点继续向下走可以收获的最大值;
那么可以分成两种情况:
第一种:t[i]没有等于3的,那么就是找一个最大的节点a[i],然后所有的g[i]都可以得到;
第二种:有一个节点u可以额外的拿到,但是必须返回的点是t[i]=3才可以,这样的话就是h[u]+sum(g(不包括u这个点))+max(a[i](不包括u)),这样找出a[i]的次大值和最大值,就可以o(n)的来枚举了
2021 ICPC 南京 H-Crystalfly(树上DP) - FJ-Frank - 博客园 (cnblogs.com)
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int inf=1e18;
const int N = 4e6+10;
const int B=2e6;
int T,n,a[100005],t[100005],f[100005],g[100005],h[100005];
int head[200005],cnt;
struct Edge
{
int next,to;
}e[200005];
void addedge(int from,int to)
{
e[++cnt].to=to;
e[cnt].next=head[from];
head[from]=cnt;
}
void dfs(int u,int fa)
{
h[u]=a[u];
int res=0,max1=0,max2=0,maxx=0;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
res+=g[v];
h[u]+=g[v];
maxx=max(maxx,a[v]);
if(t[v]==3)
{
if(max1<a[v]) max2=max1,max1=a[v];
else if(max2<a[v]) max2=a[v];
}
}
g[u]=maxx+res;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa) continue;
if(a[v]!=max1||t[v]!=3) g[u]=max(g[u],res-g[v]+h[v]+max1);
else g[u]=max(g[u],res-g[v]+h[v]+max2);
}
f[u]=a[u]+g[u];
}
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>t[i];
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
addedge(u,v);
addedge(v,u);
}
dfs(1,0);
cout<<f[1]<<endl;
for(int i=1;i<=2*n;i++) head[i]=0,e[i].next=e[i].to=0;
cnt=0;
}
system("pause");
return 0;
}
J - Xingqiu's Joke 记忆化搜索,数学
c是a,b的差,那么a,b的公共素因子也会是c的素因子,但反过来好像就不是了;
能除素因子就除素因子,可以先求出c的素因子,然后记忆化搜索,其实a和b只要最小的一个作为参数就可以了,因为他们的操作是一样的,最小的到1了就会满足条件了,假设a是最小的,如果能a能整除c的素因子d就求出a除以d下取整所需要的步数tmp1,a除以d上取整所需要的步数tmp2,以及a直接减到1所需要的步数res,最后遍历完所有因子求出一个最小的就是当前的a和c所需要的最小步数;
J. Xingqiu's Joke_哔哩哔哩_bilibili
int t;
vector<int>v;
void pri(int c)
{
int x=c;
for(int i=2;i*i<=c;i++)
{
if(x%i==0)
{
v.push_back(i);
while(x%i==0) x/=i;
}
}
if(x>1) v.push_back(x);
}
map<pair<int,int>,int>dp;
int dfs(int a,int c)
{
if(a==1) return 0;
if(c==1) return a-1;
if(dp[{a,c}]) return dp[{a,c}];
int res=a-1;
for(auto d:v)
{
if(c%d==0)
{
int rest=a%d;
int tmp1=rest+1+dfs(a/d,c/d);
int tmp2=(d-rest)+1+dfs(a/d+1,c/d);
res=min({res,tmp1,tmp2});
}
}
return dp[{a,c}]=res;
}
signed main()
{
// cin.tie(0);
// cout.tie(0);
// ios::sync_with_stdio(0);
cin>>t;
while(t--)
{
int a,b,c;
dp.clear();
v.clear();
cin>>a>>b;
c=abs(a-b);
pri(c);
int ans=dfs(min(a,b),c);
cout<<ans<<endl;
}
system("pause");
return 0;
}
1486C1 - Guessing the Greatest (easy version) 交互+二分
一开始l=1,r=n,先询问l,r的次大值的位置x,然后看看x是在l,mid之间还是mid到r之间,假设在l,mid之间,然后再去询问一遍x所在的区间,看看新的l,mid次大值的位置y是否等于x,如果不等于说明最大值没有在这半个区间内,这个区间就没有用了直接l=mid+1,否则就让r=mid,因为mid这个位置上可能是最大值所以不能丢,一直二分到l==r这样就可以直接输出答案,也可能最后是l==r-1的情况,这样单独判一边得出答案break掉就可以了
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int N = 4e6+10;
const int B=2e6;
int n;
signed main()
{
// cin.tie(0);
// cout.tie(0);
// ios::sync_with_stdio(0);
cin>>n;
int l=1,r=n,ans=0;
while(l<r)
{
cout<<"? "<<l<<" "<<r<<endl;
cout.flush();
int x;
cin>>x;
if(l==r-1)
{
if(x==l) ans=r;
else ans=l;
break;
}
int mid=l+r>>1;
if(x>=l&&x<=mid)
{
cout<<"? "<<l<<" "<<mid<<endl;
cout.flush();
int y;
cin>>y;
if(x!=y) l=mid+1;
else r=mid,ans=mid;
}
else
{
cout<<"? "<<mid<<" "<<r<<endl;
cout.flush();
int y;
cin>>y;
if(x!=y) r=mid-1;
else l=mid,ans=mid;
}
if(l==r) ans=l;
}
cout<<"! "<<ans<<endl;
cout.flush();
system("pause");
return 0;
}