(4/4)欢乐AK
以下所有代码加上本人前面的头文件(如下)就可以跑啦~
#pragma GCC optimize("O3")
#pragma GCC target("avx2")
#include
#define ll long long
#define maxn 1000005
#define inf 1e9
#define ins insert
#define pb push_back
#define vi vector <int>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
inline ll read()
{
ll x=0,w=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') w=-1; c=getchar();}
while(c<='9'&&c>='0') {x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
return w==1?x:-x;
}
题目大意:给出n个数,让你选出一个最大的子集,使得任意两个数的差>1
n < = 1 e 5 , a [ i ] < = 2 e 5 n<=1e5,a[i]<=2e5 n<=1e5,a[i]<=2e5
显然直接用 d p [ i ] dp[i] dp[i]表示现在最大的数选到 i i i,最大的子集是多少,然后直接转移即可。
代码如下:
int dp[maxn],n,a[maxn],v[maxn];
int main()
{
n=read(); rep(i,1,n) a[i]=read(),v[a[i]+1]=1;
rep(i,2,200001) dp[i]=max(dp[i-1],dp[i-2]+v[i]);
cout<<dp[200001]<<endl;
return 0;
}
题目大意:给出长度为n的数组,可以翻转任意一段,问翻转一段之后的数组最大子段和为多少
n < = 1 e 5 , − 1 e 3 < = a [ i ] < = 1 e 3 n<=1e5,-1e3<=a[i]<=1e3 n<=1e5,−1e3<=a[i]<=1e3
可以发现:翻转操作只是相当于,我们可以选择两个子段进行统计答案。
所以我们从前往后做一遍存到 d p 1 dp1 dp1,从后往前做一遍存存到 d p 2 dp2 dp2,分别表示,从该位开始的最大子段和为多少。
然后我们记录一下 d p 1 dp1 dp1数组的前缀 m a x max max,就可以直接统计答案了
代码如下:
ll n,a[maxn],dp1[maxn],dp2[maxn],ans,pmx[maxn];
int main()
{
n=read(); rep(i,1,n) a[i]=read();
rep(i,1,n) dp1[i]=max(0ll,dp1[i-1])+a[i],pmx[i]=max(pmx[i-1],dp1[i]);
per(i,n,1) dp2[i]=max(dp2[i+1],0ll)+a[i];
rep(i,1,n) ans=max(ans,pmx[i]+dp2[i+1]);
cout<<ans<<endl;
return 0;
}
题目大意:有一个 n ∗ n ∗ n n*n*n n∗n∗n大小的立方体,进行m次操作,每次操作选择从x或y或z的一个面进行切割(给定操作,例如给x=4就是沿着x=4的面进行切割),问m次操作时,每次操作后最大体积的立方块大小。
n , m < = 1000 n,m<=1000 n,m<=1000
可以发现,xyz三个维度是独立的,我们每次操作就相当于,在xyz三个线段中选择一个线段从中间断开。
那么我们维护一下这个过程,每次统计答案就是最大的 x x x*最大的 y y y*最大的 z z z了。
本人直接使用了set维护,简单暴力,当然应该有更好的维护方法。
代码如下:
int n,m,a[maxn],A[4];
char opt[maxn];
struct node{int l,r;};
inline bool operator < (node a,node b)
{
if(a.l!=b.l) return a.l<b.l;
return a.r<b.r;
}
multiset <node> sx[4];
multiset <node>::iterator it;
int main()
{
n=read(); m=read(); rep(i,1,m) cin>>opt[i]; rep(i,1,m) a[i]=read();
rep(i,0,2) sx[i].ins({0,n}),A[i]=n;
rep(i,1,m)
{
int id;
if(opt[i]=='x') id=0;
else if(opt[i]=='y') id=1;
else id=2;
for(it=sx[id].begin();it!=sx[id].end();it++)
{
node tmp=*it;
if(a[i]>=tmp.l&&a[i]<=tmp.r)
{
sx[id].ins({tmp.l,a[i]});
sx[id].ins({a[i],tmp.r});
sx[id].erase(it); break;
}
}
A[id]=0;
for(it=sx[id].begin();it!=sx[id].end();it++)
{
node tmp=*it; //cout<
A[id]=max(A[id],tmp.r-tmp.l);
}
cout<<A[0]*A[1]*A[2]<<endl;
}
return 0;
}
题目大意:给你q次区间加,区间求和的操作,需要你重新排列一下原数组,使得每次查询区间和的答之和最大。
n < = 1000 , q < = 500 n<=1000,q<=500 n<=1000,q<=500
发现只需要统计一下每个位置的数对答案的贡献就ok了。
所以我们每次区间加,就是区间内每个点的贡献+1,然后区间加操作照常就好。
然后根据贪心思想,贡献越多的地方,应该放的数就越大,排个序统计贡献就做完了。
然后由于这个数据范围很小,并不需要真的写一个线段树,暴力处理就ok了。
代码如下:
ll n,Q,a[maxn],cnt[maxn],ans,nw[maxn];
int main()
{
n=read(); Q=read(); rep(i,1,n) a[i]=read();
while(Q--)
{
int opt=read();
if(opt==1)
{
int l=read(),r=read();
rep(i,l,r) ans+=nw[i],cnt[i]++;
}
else
{
int l=read(),r=read(),w=read();
rep(i,l,r) nw[i]+=w;
}
}
sort(cnt+1,cnt+n+1); sort(a+1,a+n+1);
rep(i,1,n) ans+=a[i]*cnt[i];
cout<<ans<<endl;
return 0;
}
END