省队集训Round2 DAY1
2017-08-19 20:36
471 查看
T1
题解
对于每个位置都可以暴力的找到最右边的一个点使之后的点再异或异或和下降。可以维护一颗主席树,外层表示的是起点,内层表示的是以该点为起点的所以终点的合法区间。
每次利用前缀和作差即可。因为是区间操作所以我们标记永久化一下。
代码
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #define N 100003 using namespace std; struct data{ int ls,rs,x,sum; }tr[N*60]; int a ,n,m,sz,root ; void update(int i,int lx,int rx) { int l=tr[i].ls; int r=tr[i].rs; tr[i].sum=tr[i].x*(rx-lx+1); if (l) tr[i].sum+=tr[l].sum; if (r) tr[i].sum+=tr[r].sum; } void insert(int &i,int j,int l,int r,int ll,int rr) { i=++sz; tr[i]=tr[j]; if (ll<=l&&r<=rr) { tr[i].x+=1; update(i,l,r); return; } if (l==r) return; int mid=(l+r)/2; if (ll<=mid) insert(tr[i].ls,tr[j].ls,l,mid,ll,rr); if (rr>mid) insert(tr[i].rs,tr[j].rs,mid+1,r,ll,rr); update(i,l,r); } int qjchange(int i,int j,int l,int r,int ll,int rr,int v) { if (ll<=l&&r<=rr) return tr[i].sum-tr[j].sum+(r-l+1)*v; int mid=(l+r)/2; int ans=0; if (ll<=mid) ans+=qjchange(tr[i].ls,tr[j].ls,l,mid,ll,rr,v+tr[i].x-tr[j].x); if (rr>mid) ans+=qjchange(tr[i].rs,tr[j].rs,mid+1,r,ll,rr,v+tr[i].x-tr[j].x); return ans; } int main() { freopen("a.in","r",stdin); freopen("my.out","w",stdout); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) { int x=0; int pos=i; for (int j=i;j<=n;j++) if ((x^a[j])>=x) x^=a[j],pos=j; else break; // cout<<i<<" "<<pos<<endl; insert(root[i],root[i-1],1,n,i,pos); } scanf("%d",&m); int ans=0; for (int i=1;i<=m;i++) { int l,r; scanf("%d%d",&l,&r); l=(l+ans)%n+1; r=(r+ans)%n+1; // cout<<l<<" "<<r<<endl; if (l>r) swap(l,r); ans=qjchange(root[r],root[l-1],1,n,l,r,0); printf("%d\n",ans); } }
T2
题解
从高的开始消,消到左右两边较高的位置,然后把他们合并成一大列一起向下消。关键就是怎么划分,对于每一大列维护del[i]表示的是如果当前阶段继续向下消,上面还有多少空可以补。
代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<set> #include<vector> #define N 200003 #define LL long long using namespace std; set<pair<long long,int> > p; LL k,f ,h ,del ; int n,l ,r ,fa ; int find(int x) { if (fa[x]==x) return x; fa[x]=find(fa[x]); return fa[x]; } int main() { freopen("a.in","r",stdin); scanf("%d%lld",&n,&k); for (int i=1;i<=n;i++) scanf("%lld",&h[i]); for (int i=1;i<=n;i++) { p.insert(make_pair(h[i],i)); l[i]=r[i]=fa[i]=i; } LL ans=0; while (p.size()) { int x=(*--p.end()).second; p.erase(--p.end()); LL L=l[x]-1==0?0:h[find(l[x]-1)]; LL R=r[x]+1==n+1?0:h[find(r[x]+1)]; LL hi=max(L,R); LL sum=(LL)(r[x]-l[x]+1)*(h[x]-hi); if (del[x]>=sum) del[x]-=sum; else sum-=del[x],ans+=(sum-1)/k+1,del[x]=(k-sum%k)%k; h[x]=hi; if (hi!=0) { if (L==hi) { int t=find(l[x]-1); del[x]+=del[t]; p.erase(make_pair(L,t)); fa[t]=x; l[x]=l[t]; } if (R==hi) { int t=find(r[x]+1); del[x]+=del[t]; p.erase(make_pair(R,t)); fa[t]=x; r[x]=r[t]; } p.insert(make_pair(h[x],x)); } } printf("%lld\n",ans); }
T3
题解
首先需要知道斐波那契数列的一个性质gcd(f[a],f[b])=f[gcd(a,b)]这个怎么证明?先证明gcd(f[a],f[a+1])=1
f[0]=1,f[1]=1,gcd(f[0],f[1])=1
利用更相减损,gcd(f[a],f[a+1])=gcd(f[a],f[a]+f[a−1])=gcd(f[a],f[a−1]),最终得到gcd(f[a],f[a+1])=gcd(f[0],f[1])
还需要用到一个式子f[n+m]=f[n+1]f[m]+f[n]f[m−1]
这道题有一种有趣的组合的证明方法(摘自小火车的论文)
gcd(f[a],f[b])=gcd(f[a],f[a]f[b−a+1]+f[a−1]f[b−a])=gcd(f[a],f[a−1]f[b−a])
因为gcd(f[a],f[a−1])=1,那么gcd(f[a],f[b])=gcd(f[a],f[b−a]),可以递归,就是辗转相除嘛。那么上面的问题得证。
lcm(f[a],f[b])=f[a]f[b]f[gcd(a,b)]
设读入的下标构成的集合为S,那么lcm=∏T∈S,T≠∅f[gcd(T)](−1)|T|−1,其中gcd(T)表示的是集合T中所有数的最大公约数
然后考虑枚举gcd(T)=i
lcm=∏i=1maxf[i]∑T∈S,T≠∅,gcd(T)=i(−1)|T|−1
式子的瓶颈在于gcd(T)=i,我们可以利用莫比乌斯反演进行转化。
设g(i)=∑T∈S,T≠∅,gcd(T)=i(−1)|T|−1,h(i)=∑T∈S,T≠∅,i|gcd(T)(−1)|T|−1
T≠∅比较讨厌,我们发现空集比较讨厌,考虑直接提出来。然后再把指数中的−1提出来,式子可以化简成h(i)=1−∑T∈S,i|gcd(T)(−1)|T|
∑T∈S,i|gcd(T)(−1)|T|=∑|S|i=0(−1)i∗C(|S|,i),C表示组合数
其实就是杨辉三角中某一行的偶数列的和-奇数列的和,在|S|>0是式子的值恒等于0
所以h(i)=[下标中会否有i的倍数]
根据莫比乌斯反演g(n)=∑d|nh(d)∗μ(dn)
那么lcm=∏i=1maxf[i]g[i]可以用O(nlogn)的时间预处理g[i]数组,然后再用快速幂求解。
代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 1000003 #define p 1000000007 #define LL long long using namespace std; int mu ,a ,cnt ,h ,g ,n,pd ,prime ,f ; LL quickpow(LL num,int x) { LL ans=1,base=num%p; x=(x%(p-1)+p-1)%(p-1); while (x) { if (x&1) ans=ans*base%p; x>>=1; base=base*base%p; } return ans; } void init(int n) { mu[1]=1; for (int i=2;i<=n;i++) { if (!pd[i]) { prime[++prime[0]]=i; mu[i]=-1; } for (int j=1;j<=prime[0];j++) { if (prime[j]*i>n) break; pd[i*prime[j]]=1; if (i%prime[j]==0) break; mu[i*prime[j]]=-mu[i]; } } f[0]=0; f[1]=1; for (int i=2;i<=n;i++) f[i]=(f[i-1]+f[i-2])%p; for (int i=1;i<=n;i++) { if (!h[i]) continue; for (int j=i;j<=n;j+=i) g[i]+=h[j]*mu[j/i]; } } int main() { freopen("a.in","r",stdin); scanf("%d",&n); int mx=0; for (int i=1;i<=n;i++) scanf("%d",&a[i]),mx=max(a[i],mx); for (int i=1;i<=n;i++) { for (int x=1;x*x<=a[i];x++) if (a[i]%x==0) h[x]=1,h[a[i]/x]=1; } init(mx); LL ans=1; for (int i=1;i<=mx;i++) ans=ans*quickpow(f[i],g[i])%p; printf("%lld\n",ans); }
相关文章推荐
- 省队集训 Day1 残缺的字符串
- 省队集训Round2 DAY2
- 省队集训Day1 过河
- 省队集训Round2 DAY3
- 省队集训Round2 DAY7
- 省队集训Round3 DAY1
- 省队集训Day1 总统选举
- 省队集训Day1 睡觉困难综合征
- 省队集训day6 C
- bzoj4171 or 省队集训day3 chess: Rhl的游戏
- CCNU ACM 2016夏季集训·day1比赛
- 【集训Day1 测试】奇怪数
- 省队集训DAY3
- 北京集训②DAY1 Afternoon
- 考前集训(day1上)
- 福建省队集训被虐记——DAY1
- 集训day1 p1
- 【BZOJ 4598】【SDOI 2016 Round2 Day1 T3】模式字符串
- FJ省队集训DAY1 T1
- loj6030 「雅礼集训 2017 Day1」矩阵