noip模拟74[套路]
noip模拟74 solutions
其实对于这么多次的考试来说,我发现了一个事实:
为什么 Amx 这么智障 ..?
为什么 AaMuXiiiiii 这么这么的傻逼傻逼傻逼傻逼傻逼 ..?
我发现,总是有一些套路非常相似的题出现
但是我总是做不会,我也不知道为啥就总是记不住那些题(别人都能记得清清楚楚)
所以这就是我今天第一题没有做出来的原因???
今天吓的我就上了一趟厕所!!!水都没敢接。。。
T1 自然数
这种在一个序列上找区间维护某个值的和的题
一般都是线段树,枚举某个端点,用线段树维护另一个端点的值
都是插入某个值或者删除某个值会对这些值有影响,但是这些影响可以直接区间覆盖
这个题也不例外
首先对于左端点一定的时候,随着右端点的右移,\(mex\)值是单调不降的
那么我们就枚举左端点,用线段树维护右端点
先把所有以\(1\)为左端点的区间的\(mex\)值求出来,这个可以\(\mathcal{O(n)}\)做到
然后随着左端点的右移,慢慢的删去一些点,这些点可以用来更新后面值比他大的区间
由于\(mex\)是单调不降的,可以直接在线段树上二分得到这个区间
注意在序列中可能会有和当前删掉的点的值相同的点,这个点以后的\(mex\)我是不可以更新的
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=2e5+5;
int n,a[N],ans;
struct XDS{
#define ls x<<1
#define rs x<<1|1
int sum[N*8],mn[N*8],tag[N*8];
XDS(){memset(tag,-1,sizeof(tag));}
void pushup(int x){
sum[x]=sum[ls]+sum[rs];
mn[x]=min(mn[ls],mn[rs]);
return ;
}
void pushdown(int x,int l,int r){
if(tag[x]==-1)return ;
int mid=l+r>>1;
tag[ls]=tag[rs]=tag[x];
mn[ls]=mn[rs]=tag[x];
sum[ls]=tag[x]*(mid-l+1);
sum[rs]=tag[x]*(r-mid);
tag[x]=-1;
return ;
}
void ins(int x,int l,int r,int ql,int qr,int v){
if(ql>qr)return ;
if(ql<=l&&r<=qr){
tag[x]=v;mn[x]=v;sum[x]=(r-l+1)*v;
return ;
}
pushdown(x,l,r);
int mid=l+r>>1;
if(ql<=mid)ins(ls,l,mid,ql,qr,v);
if(qr>mid)ins(rs,mid+1,r,ql,qr,v);
pushup(x);return ;
}
int sm,pos;
void query(int x,int l,int r,int qr){
if(~pos||l>qr)return ;
pushdown(x,l,r);
int mid=l+r>>1;
if(r<=qr){
if(l==r){
if(mn[x]<=sm)pos=l+1;
return ;
}
if(mn[rs]<=sm)query(rs,mid+1,r,qr);
else query(ls,l,mid,qr);
pushup(x);
return ;
}
query(rs,mid+1,r,qr);
query(ls,l,mid,qr);
pushup(x);return ;
}
int Sum(int x,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return sum[x];
pushdown(x,l,r);
int mid=l+r>>1,ret=0;
if(ql<=mid)ret+=Sum(ls,l,mid,ql,qr);
if(qr>mid)ret+=Sum(rs,mid+1,r,ql,qr);
pushup(x);return ret;
}
#undef ls
#undef rs
}xds;
int vis[N],p;
int now[N],lst[N];
signed main(){
#ifdef oj
freopen("mex.in","r",stdin);
freopen("mex.out","w",stdout);
#endif
scanf("%lld",&n);
fo(i,1,n)scanf("%lld",&a[i]);
p=0;fo(i,1,n){
if(a[i]<=200000){
if(vis[a[i]])lst[now[a[i]]]=i;
vis[a[i]]=true;
now[a[i]]=i;
}
lst[i]=n+1;
while(vis[p])p++;
xds.ins(1,1,n,i,i,p);
ans+=p;//cout<<p<<endl;
}
fo(i,2,n){
xds.sm=a[i-1];xds.pos=-1;
xds.query(1,1,n,lst[i-1]-1);
//cout<<lst[i-1]-1<<" "<<xds.pos<<endl;
xds.pos=max(xds.pos,i);
xds.ins(1,1,n,xds.pos,lst[i-1]-1,a[i-1]);
ans+=xds.Sum(1,1,n,i,n);
}
printf("%lld",ans);
}
T2 钱仓
这就是一个超级大贪心,考场上口胡了一个就过了
结论:
每一个仓库一定会将自己的金币放到顺时针下离自己最近的仓库,无论那个仓库有没有金币
每个仓库最多被放一个金币,因为每个金币只能运一次,我把别人放到这里的留下,自己的拿走
枚举的起始点一定可以使得后面所有的仓库都不会是空的(指的是没有被别的仓库给金币),如果是空的,那么这个就要从后面的仓库拿金币,这样的话,后面的仓库就不符合第一个结论了
这个序列一定存在上面这样的起始点,且有且仅有一个,如果没有的话就无解,因为你永远放不够,如果有多个,那么金币会多,不能恰好放到每一个仓库
这样的起始点非常好找,直接从\(1\)往后枚举,如果出现不够的情况就停止
将起点更新为下一个点,接着枚举,枚举到\(n\)就够了
计算的时候用一个变量保存我金币已经放到哪里了,直接往后加就行了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=2e5+5;
int n,c[N],ans;
int sum,dc,now;
int pfh[N];
signed main(){
#ifdef oj
freopen("barn.in","r",stdin);
freopen("barn.out","w",stdout);
#endif
scanf("%lld",&n);
fo(i,1,n)pfh[i]=pfh[i-1]+i*i;
fo(i,1,n)scanf("%lld",&c[i]),c[i+n]=c[i];
sum=dc=0;
fo(i,1,n){
sum+=c[i];
if(sum<i-dc)sum=0,dc=i;
}
fo(i,1,n)c[i]=c[i+dc];
//cout<<dc<<endl;
now=0;
fo(i,1,n){
if(now>=i)ans+=pfh[now-i+c[i]]-pfh[now-i],now=now+c[i];
else ans+=pfh[c[i]-1],now=now+c[i];
}
printf("%lld",ans);
}
T3 游戏
话说这个真的好妙啊
我一遇到这样的最优策略的题就懵了,老是找不到该往哪里想
首先找到最优策略,两个人决策一定相同。。
如果\(A\)想拿这个硬币,因为可以赢的话,那么\(B\)就不想让\(A\)拿到这个金币,所以\(B\)也想拿到
然后我们先看一看只有一颗石子的情况
这个时候拿到石子的概率是个无穷等比数列,求和公式是\(\frac{a_1}{1-q}\)(a是序列,q是公差)
所以如果想拿到石子的话,先手就有这么大的可能性拿到石子,后手直接用\(1\)减它就行了
我们设\(a_i\)为有\(i\)个石子\(A\)先手时\(A\)胜的概率,\(b_i\)为\(i\)个石子\(B\)先手时\(A\)胜的概率
可以根据定义得到\(a_0=0\;b_0=1\)
还可以得到转移:(自己推导)
然后你发现取模意义下无法比较大小,但是你发现这大小关系是交错的
也就是下面转移一次,上面转移一次,先把这两个矩阵乘一下,再快速幂,最后多出来的再乘一遍
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int bas=1e8;
const int mod=1e9+7;
int t,n,p,q;
int a[1005],b[1005];
int ksm(int x,int y){
int ret=1;x=(x+mod)%mod;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
struct matrix{
int x[2][2];
matrix(){memset(x,0,sizeof(x));}
matrix operator * (matrix a)const{
matrix ret;
fo(i,0,1)fo(j,0,1)fo(k,0,1)ret.x[i][j]=(ret.x[i][j]+x[i][k]*a.x[k][j])%mod;
return ret;
}
}ma,odd,eve;
matrix mksm(matrix x,int y){
matrix ret;ret.x[0][0]=1;ret.x[1][1]=1;
while(y){
if(y&1)ret=ret*x;
x=x*x;y>>=1;
}return ret;
}
signed main(){
#ifdef oj
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
#endif
scanf("%lld",&t);
while(t--){
scanf("%lld%lld%lld",&n,&p,&q);
p=p*ksm(bas,mod-2)%mod;
q=q*ksm(bas,mod-2)%mod;
ma.x[0][0]=0;ma.x[0][1]=1;
eve.x[0][0]=p*(1+mod-q)%mod*ksm(1+mod-p*q%mod,mod-2)%mod;
eve.x[1][0]=(1+mod-p)*ksm(1+mod-p*q%mod,mod-2)%mod;
eve.x[0][1]=(1+mod-q)*ksm(1+mod-p*q%mod,mod-2)%mod;
eve.x[1][1]=q*(1+mod-p)%mod*ksm(1+mod-p*q%mod,mod-2)%mod;
odd.x[0][0]=q*(1+mod-p)%mod*ksm(p+q+mod-p*q%mod,mod-2)%mod;
odd.x[1][0]=p*ksm(p+q+mod-p*q%mod,mod-2)%mod;
odd.x[0][1]=q*ksm(p+q+mod-p*q%mod,mod-2)%mod;
odd.x[1][1]=p*(1+mod-q)%mod*ksm(p+q+mod-p*q%mod,mod-2)%mod;
ma=ma*mksm(odd*eve,n>>1);
if(n&1)ma=ma*odd;
printf("%lld\n",ma.x[0][0]);
}
}
T4 Sanrd
不会