广州二中NOI2018模拟赛(三十六)
A.染色游戏

19分是很好搞的,爆搜一下就出来了
搜的时候传一个参,表示前一个选择的a是多少,然后接下来的数分一个不选的支,如果它不小于前一个选择的a就再分一个选的支,然后乱搞搞就好了
复杂度O(2^n)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL a[50],n,ans=-1e9;
void dfs(int p,int pre,LL now,int ps)
{
if(p>n){ans=max(ans,now-(LL)ps*(ps+1)/2);return;}
dfs(p+1,pre,now,ps+1);
if(a[p]>=pre) dfs(p+1,a[p],now+a[p]-(LL)ps*(ps+1)/2,0);
}
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld",a+i);
dfs(1,-1e9,0,0);
printf("%lld\n",ans);
return 0;
}
41分也是很好搞的
设dp[i]表示选择a[i]能达到的最大值,那转移就是:
dp[i]=max{dp[j]+a[i]-(i-j-1)*(i-j)/2},转移前提是a[j]≤a[i]
O(n²)大力枚举即可
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF=1ll<<60;
LL n,a[5010];
LL f[5010];
int main()
{
scanf("%lld",&n);
for(LL i=1;i<=n;i++) scanf("%lld",a+i);
a[0]=-INF;
for(LL i=1;i<=n+1;i++) f[i]=-INF;
for(LL i=1;i<=n;i++)
for(LL j=0;j<i;j++)
if(a[i]>=a[j])
f[i]=max(f[i],f[j]+a[i]-(i-j-1)*(i-j)/2);
LL ans=-INF-INF;
for(LL i=0;i<=n;i++)
ans=max(ans,f[i]-(n-i)*(n-i+1)/2);
printf("%lld\n",ans);
return 0;
}
并不知道子任务3有什么用,直接来考虑正解吧
假设没有ai≤aj的限制?
那么将上面那个dp方程稍作变换,得到:
发现式子中很多东西都是定值,我们把定值去掉,问题就变成了最大化下面这个柿子的值:
我们把j看做x,把dp[j]-j(j+1)/2看做y,柿子就变成了
调整一下位置,变成
于是问题变成了:求一条直线与y轴截距的最大值,这条直线与y=-ix平行,且经过某个点(x0,y0)
容易看出这样的点(x0,y0)一定在上凸壳上,于是我们可以维护一个上凸壳,然后就可以搞了
那么加上ai≤aj的限制?
显然这里存在一个偏序关系,j号点对i号点的答案产生影响,当且仅当j≤i且aj≤ai
用CDQ分治来维护这个偏序关系即可(听说用数据结构维护会被卡常?)
由于答案具有单调性,所以可以用单调队列来维护,这样每段分治做询问就是线性的了
分治过程中用归并排序来维护偏序,用sort会多一个log导致TLE
最后的答案就是max{dp[i]-(n-i)*(n-i+1)/2}
时间复杂度O(n log n)
#include<bits/stdc++.h>
using namespace std;
const int S=1<<16;
char buf[S],*H,*T;
inline char Get()
{
if(H==T) T=(H=buf)+fread(buf,1,S,stdin);
if(H==T) return -1;return *H++;
}
inline int read()
{
int x=0,fg=1;char c=Get();
while(!isdigit(c)&&c!='-') c=Get();
if(c=='-') fg=-1,c=Get();
while(isdigit(c)) x=x*10+c-'0',c=Get();
return fg*x;
}
typedef long long LL;
const int N=1000010;
const LL INF=1ll<<60;
LL n,f[N],v[N];
struct Point{LL x,y;} a[N],tmp[N],q[N];
int que[N],head,tail;
bool operator < (const Point &a,const Point &b){return a.x<b.x||a.x==b.x&&a.y<=b.y;}
double slope(int p1,int p2)
{
double x1=p1,x2=p2,y1=f[p1]-x1*(x1+1)/2,y2=f[p2]-x2*(x2+1)/2;
return (y2-y1)/(x2-x1);
}
void gao(int L,int R)
{
if(L==R) return;
int M=(L+R)/2;
int p1=L,p2=M+1;
for(int i=L;i<=R;i++)
if(q[i]<a[M]) tmp[p1++]=q[i];
else tmp[p2++]=q[i];
for(int i=L;i<=R;i++) q[i]=tmp[i];
gao(L,M);
p1=L;p2=M+1;head=1;tail=0;
while(p2<=R)
if(p1<=M&&q[p1].y<q[p2].y)
{
while(head<tail&&slope(que[tail-1],que[tail])<slope(que[tail-1],q[p1].y)) tail--;
que[++tail]=q[p1].y;p1++;
}
else if(p2<=R)
{
while(head<tail&&slope(que[head],que[head+1])>-q[p2].y) head++;
if(head<=tail)
{
LL i=q[p2].y,j=que[head];
f[q[p2].y]=max(f[i],f[j]+v[i]-(i-j-1)*(i-j)/2);
}
p2++;
}
gao(M+1,R);
p1=L;p2=M+1;int now=L;
while(p1<=M&&p2<=R)
if(p1<=M&&q[p1].y<q[p2].y) tmp[now++]=q[p1++];
else tmp[now++]=q[p2++];
while(p1<=M) tmp[now++]=q[p1++];
while(p2<=R) tmp[now++]=q[p2++];
for(int i=L;i<=R;i++) q[i]=tmp[i];
}
int main()
{
n=read();
for(LL i=1;i<=n;i++)
v[i]=a[i].x=read(),a[i].y=i,q[i]=a[i];
for(LL i=1;i<=n;i++) f[i]=v[i]-i*(i-1)/2;
sort(a+1,a+n+1);
gao(1,n);
LL ans=-INF;
for(LL i=0;i<=n;i++)
ans=max(ans,f[i]-(n-i)*(n-i+1)/2);
printf("%lld\n",ans);
return 0;
}
B.异或II


20分还是很好拿的,只要每次操作先算一遍前缀异或和,每次操作就是O(n)的了,总复杂度O(Tn)
#include<bits/stdc++.h>
using namespace std;
const int N=2010;
int A[N],B[N];
int main()
{
int n,k,t;
scanf("%d%d%d",&n,&k,&t);
for(int i=0;i<n;i++) scanf("%d",A+i);
while(t--)
{
for(int i=n;i<2*n;i++) A[i]=A[i-n];
for(int i=1;i<2*n;i++) A[i]^=A[i-1];
B[0]=A[k-1];
for(int i=1;i<n;i++)
B[i]=A[i+k-1]^A[i-1];
for(int i=0;i<n;i++) A[i]=B[i];
}
for(int i=0;i<n;i++) printf("%d ",A[i]);
return 0;
}
然后打表,发现第2的次方次操作之后的序列很有规律,然后倍增搞它一下就出来了
甚至连什么FFT、MTT之类的狗屎东西都不用写。代码也很短,我的代码除掉IO优化就40行
复杂度O(n log n)
#include<bits/stdc++.h>
using namespace std;
namespace IO
{
const int S=1<<16;
//Input Correlation
char buf[S],*H,*T;
inline char Get()
{
if(H==T) T=(H=buf)+fread(buf,1,S,stdin);
if(H==T) return -1;return *H++;
}
template<class I>
inline I read()
{
I x=0;char c=Get();
while(!isdigit(c)) c=Get();
while(isdigit(c)) x=x*10+c-'0',c=Get();
return x;
}
//Output Correlation
char obuf[S],*oS=obuf,*oT=oS+S-1,c,qu[55];int qr;
inline void flush()
{
fwrite(obuf,1,oS-obuf,stdout);
oS=obuf;
}
inline void putc(char x)
{
*oS++ =x;
if(oS==oT) flush();
}
inline void print(int &x)
{
if(!x) putc('0');
while(x) qu[++ qr]=x%10+'0',x /= 10;
while(qr) putc(qu[qr--]);
putc(' ');
}
}
typedef long long LL;
using namespace IO;
const int N=500010;
int n,k,a[N];LL t;
bool vis[N];
int f[N],g[N];
void gao(int x,int d)
{
int cnt=0;
while(!vis[x])
{
f[++cnt]=x;vis[x]=1;
g[cnt]=g[cnt-1]^a[x];
x=(x+d>=n)?(x+d-n):(x+d);
}
int m1=k/cnt,m2=k%cnt;
for(int i=1;i<=cnt;i++)
{
a[f[i]]=(m1&1)?g[cnt]:0;
a[f[i]]^=(m2<=cnt-i+1)?(g[i+m2-1]^g[i-1]):(g[cnt]^g[i-1]^g[i+m2-cnt-1]);
}
}
int main()
{
n=read<int>();k=read<int>();t=read<LL>();
for(int i=0;i<n;i++) a[i]=read<int>();
for(LL i=1,d=1;i<=t;i<<=1,d=(d<<1)%n)
{
if(!(t&i)) continue;
memset(vis,0,sizeof(vis));
for(int j=0;j<n;j++)
if(!vis[j]) gao(j,d);
}
for(int i=0;i<n;i++) print(a[i]);
flush();
return 0;
}
C.商店街派对


这题怎么做啊……不会啊
先留一个坑吧,下面是我抄的代码……
#include<bits/stdc++.h>
#define pLL pair<LL,LL>
#define pb push_back
using namespace std;
typedef long long LL;
LL read()
{
LL x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const LL N=200010;
LL n,m,mx=0;
vector<pLL> sec[N];
LL sum,tot=0;
LL v[N],f[N],cnt[N];
LL p[N],fa[N];
inline LL find(LL x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void gao(LL x,bool fg)
{
LL y;
while(find(x)&&v[x]>0)
{
y=find(x)-1;
v[x]+=v[y];
fa[fa[x]]=fg?fa[y]:y;
p[find(x)]=x;
}
}
void Add(LL x,LL k)
{
sum-=k;
if(x==p[find(x)]) x++;
x=p[find(x)];
v[x]+=k;gao(x,0);
}
pLL gao(LL x)
{
sum=0;
for(LL i=0;i<=mx;i++)
fa[i]=p[i]=i,cnt[i]=f[i]=v[i]=0;
for(LL i=1;i<=mx;i++)
{
for(LL j=0;j<sec[i].size();j++)
Add(sec[i][j].first-1,sec[i][j].second);
f[i]=sum+v[p[0]]-x;
cnt[i]=cnt[p[0]]+1;
v[i]=f[i]-f[i-1];gao(i,1);
}
return pLL(cnt[p[0]],sum+v[p[0]]+tot);
}
int main()
{
LL l,r,c;
n=read(),m=read();
for(LL i=1;i<=n;i++)
{
l=read();r=read();c=read();
sec[r].pb(pLL(l,c));
tot+=c;mx=max(mx,r);
}
l=0;r=tot+1;LL mid;
while(l<r)
{
mid=(l+r)/2;
if(gao(mid).first<m) r=mid;
else l=mid+1;
}
printf("%lld\n",gao(l).second+l*m);
return 0;
}