头一次做图巨的模拟题OWO
自从上一次听图巨讲课然后骗了小礼物以后一直对图巨印象挺好的233
T1:
对于XY取对数=Y*log(x)
对于Y!取对数=log(1*2*3*...*Y)=log1+log2+log3+...+logY
因为数字大小不超过1e5,直接累加最后比较就可以了
#include<iostream> #include<cstdio> #include<cmath> using namespace std; int t,x,y; double a,b; int main() { freopen("yuuutsu.in","r",stdin); freopen("yuuutsu.out","w",stdout); scanf("%d",&t); while(t--){ scanf("%d%d",&x,&y); a=y*log(x); b=0; for(int i=1;i<=y;i++){ b+=log(i); } if(a<=b)printf("Yes\n"); else printf("No\n"); } return 0; }
T2:
每一次操作会让区间整体加或减->在差分数组上首位加减
列出目标序列的差分数组,可以进行操作让一个位置的数字移动k步,如果有大小相同的数字撞在一起就会消掉,不同的话可以合并。想起星空这道题,不同的是今天的T2只能走k一种步数且差分值并不只代表一种状态
目标是要让所有的值变成0,又想到一道跳斑马线的题……?考虑把位置对于k取模余数不同的数字分开处理。维护位置对于k取模后余数为下标,记录差分值之和的m数组。用树状数组维护当前的m是否都为0。
#include<iostream> #include<cstdio> using namespace std; const int N=2e6+10; int n,k,q,a[N],sum[N],m[N]; long long tree[N]; void add(int x,int y){ for(;x<=k;x+=(x&-x))tree[x]+=y; } long long ask(int x){ long long num=0; for(;x;x-=(x&-x))num+=tree[x]; return num; } int main() { freopen("august.in","r",stdin); freopen("august.out","w",stdout); scanf("%d%d%d",&n,&k,&q); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); m[i%k]+=a[i]-a[i-1]; } m[(n+1)%k]+=-a[n]; for(int i=0;i<k;i++){ add(i+1,(m[i]!=0)); } if(!ask(k))printf("Yes\n"); else printf("No\n"); for(int i=1,x,pos;i<=q;i++){ scanf("%d%d",&pos,&x); int pos0=pos%k,pos1=(pos+1)%k; int val=(m[pos0]!=0),val1=(m[pos1]!=0); m[pos0]=m[pos0]-a[pos]+a[pos-1]; m[pos1]=m[pos1]-a[pos+1]+a[pos]; a[pos]+=x; m[pos0]=m[pos0]+a[pos]-a[pos-1]; m[pos1]=m[pos1]+a[pos+1]-a[pos]; add(pos0+1,(m[pos0]!=0)-val); add(pos1+1,(m[pos1]!=0)-val1); if(!ask(k))printf("Yes\n"); else printf("No\n"); } return 0; }
发现自己对于m是否都为0的处理过于麻烦了…这是何种山路十八弯的脑回路才会想到这种处理…
其实是中途思路锅了,保留了树状数组的写法XD其实直接记一个m不为0的数量,每次m变化的时候进行更新就好了
T3:
将问题转化成,对于树上的一个点,会对多少区间产生贡献。
线段树维护子树中存在哪些位置的点,线段树下标是在a数组中的位置。如果这个点可以对一段区间产生贡献,那么这段区间在线段树种一定是连续的1,中间若存在0则代表这段区间中在更高的地方存在点。在线段树上统计答案,记录线段树每个节点从左端点开始最长的一段1的长度lonl,从右端点开始最长的一段1的长度lonr,以及包含的区间个数val。val=左儿子的val+右儿子的val+左儿子lonr*右儿子lonl(端点在两边的区间数量)。这样计算一定不重不漏,有线段树分治的意味。
维护节点x的线段树的时候,对所有儿子进行线段树合并,再把x点insert进去。注意当前节点线段树root的区间个数要减去节点儿子们的区间个数,才能用来累计答案。
#include<iostream> #include<cstdio> using namespace std; const int N=2000010; int n,tot,cnt; int ver[N],Next[N],head[N]; int a[N],b[N],pos[N],T[N],L[N*22],R[N*22],lonl[N*22],lonr[N*22]; long long ans,sum[N],val[N*22]; void add(int x,int y){ ver[++tot]=y; Next[tot]=head[x]; head[x]=tot; } void update(int p,int l,int r){ int mid=(l+r)/2; if(lonl[L[p]]==mid-l+1)lonl[p]=mid-l+1+lonl[R[p]]; else lonl[p]=lonl[L[p]]; if(lonr[R[p]]==r-mid)lonr[p]=r-mid+lonr[L[p]]; else lonr[p]=lonr[R[p]]; val[p]=val[L[p]]+val[R[p]]+1ll*lonr[L[p]]*lonl[R[p]]; } void change(int &p,int p0,int l,int r){ if(!p){ p=p0; return; } int mid=(l+r)/2; if(L[p0])change(L[p],L[p0],l,mid); if(R[p0])change(R[p],R[p0],mid+1,r); update(p,l,r); } void ins(int &p,int l,int r,int pos){ if(!p)p=++cnt; if(l==r){ lonl[p]=lonr[p]=1; val[p]=0; return; } int mid=(l+r)/2; if(pos<=mid)ins(L[p],l,mid,pos); else ins(R[p],mid+1,r,pos); update(p,l,r); } void dfs(int x){ for(int i=head[x];i;i=Next[i]){ int y=ver[i]; dfs(y); sum[x]+=sum[y]; change(T[x],T[y],1,n); } ins(T[x],1,n,pos[x]); long long num=val[T[x]]; ans+=(num-sum[x])*b[x]; sum[x]=num; } int main() { freopen("sagittarius.in","r",stdin); freopen("sagittarius.out","w",stdout); scanf("%d",&n); for(int i=2,x;i<=n;i++){ scanf("%d",&x); add(x,i); // st[i][0]=x; } for(int i=1;i<=n;i++)scanf("%d",&a[i]),pos[a[i]]=i; for(int i=1;i<=n;i++)scanf("%d",&b[i]),ans+=b[i]; dfs(1); printf("%lld\n",ans); return 0; }
原文地址:https://www.cnblogs.com/chloris/p/11768151.html