T1 漂亮大厨(cook)
先求出每次询问有多少个数小于等于 \(y\),再统计答案。
区间加,区间查小于等于某个数个数,考虑分块,块内再维护一个有序序列。
区间加:散块直接加,暴力排序重构有序序列;整块打标记。
区间小于等于某个数个数:散块暴力累加;整块在有序序列中二分查找快速统计。
这一部分时间复杂度 \(O(q\sqrt n\log n)\),有神秘做法可以做到 \(O(q\sqrt n)\),但据说实用性不大。
这时问题变为给定若干个 \(n,m\),求 \(\sum\limits_{i=0}^{m} \dbinom{n}{i}\)。为方便,记 \(C(n,m)=\dbinom{n}{m},S(n,m)=\sum\limits_{i=0}^{m} C(n,i)\)。
我们知道 \(C(n,m)=C(n-1,m-1)+C(n-1,m)\),那么:
\[S(n,m)=C(n-1,0)+\sum\limits_{i=1}^{m} C(n-1,i-1)+C(n-1,i)=2S(n-1,m)-C(n-1,m) \]变形得到:
\[S(n-1,m)=\dfrac{S(n,m)+C(n-1,m)}{2} \]\[S(n+1,m)=2S(n,m)-C(n,m) \]同时又有:
\[S(n,m-1)=S(n,m)-C(n,m) \]\[S(n,m+1)=S(n,m)+C(n,m+1) \]所以发现 \(n,m\) 可以 \(O(1)\) 加减 \(1\),用莫队维护,初始时 \(S(0,0)=1\),时间复杂度 \(O(q\sqrt W)\),\(W\) 为值域,最大为 \(max_x\times q+max_{a_i}=10^7+5\times 10^5\),数据过水,没超过 \(10^5\)。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
const int MAXN=1e5+10,MAX=1e7+5e5+10,MOD=998244353;
int n,m,a[MAXN],len,bel[MAXN],b[MAXN],e[MAXN],p[MAXN],add[MAXN],cnt;
namespace mo
{
int t,N,len,bel[MAX],ANS[MAXN];
long long P[MAX],inv[MAX],ans=1;
struct node{int n,m,pos;}p[MAXN];
inline bool cmp(node x,node y)
{
if(bel[x.n]!=bel[y.n]) return x.n<y.n;
return (bel[x.n]&1)?x.m<y.m:x.m>y.m;
}
inline long long C(int n,int m)
{
if(n<m) return 0;
return P[n]*inv[m]%MOD*inv[n-m]%MOD;
}
inline long long ksm(long long a,int b)
{
long long ans=1;
while(b)
{
if(b&1) ans=ans*a%MOD;
a=a*a%MOD,b>>=1;
}
return ans;
}
inline void work()
{
t=::cnt;
for(register int i=1;i<=t;++i)
N=max(N,p[i].n);
len=N/sqrt(t);
for(register int i=1;i<=N;++i) bel[i]=(i-1)/len+1;
sort(p+1,p+t+1,cmp);
P[0]=1;for(register int i=1;i<=N;++i) P[i]=P[i-1]*i%MOD;
inv[N]=ksm(P[N],MOD-2);for(register int i=N-1;i>=0;--i) inv[i]=inv[i+1]*(i+1)%MOD;
for(register int i=1,n=0,m=0;i<=t;++i)
{
while(n<p[i].n) ans=(ans*2-C(n,m))%MOD,++n;
while(m>p[i].m) ans=(ans-C(n,m))%MOD,--m;
while(n>p[i].n) ans=(ans+C(n-1,m))%MOD*499122177%MOD,--n;
while(m<p[i].m) ans=(ans+C(n,m+1))%MOD,++m;
ans=(ans+MOD)%MOD;ANS[p[i].pos]=ans;
}
for(register int i=1;i<=t;++i) cout<<ANS[i]<<'\n';
return ;
}
};
int main()
{
#ifdef ONLINE_JUDGE
freopen("cook.in","r",stdin);
freopen("cook.out","w",stdout);
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(0);
#endif
cin>>n>>m;len=sqrt(n);
for(register int i=1;i<=n;++i)
cin>>a[i],p[i]=a[i],bel[i]=(i-1)/len+1;
for(register int i=1;i<=bel[n];++i)
{
b[i]=(i-1)*len+1,e[i]=min(i*len,n);
sort(p+b[i],p+e[i]+1);
}
while(m--)
{
int opt,l,r,x,y,k;cin>>opt>>l>>r;
if(opt==1)
{
cin>>x;
if(bel[l]==bel[r])
{
for(register int i=l;i<=r;++i) a[i]+=x;
for(register int i=b[bel[l]];i<=e[bel[l]];++i) p[i]=a[i];
sort(p+b[bel[l]],p+e[bel[l]]+1);continue;
}
for(register int i=l;i<=e[bel[l]];++i) a[i]+=x;
for(register int i=b[bel[l]];i<=e[bel[l]];++i) p[i]=a[i];
sort(p+b[bel[l]],p+e[bel[l]]+1);
for(register int i=b[bel[r]];i<=r;++i) a[i]+=x;
for(register int i=b[bel[r]];i<=e[bel[r]];++i) p[i]=a[i];
sort(p+b[bel[r]],p+e[bel[r]]+1);
for(register int i=bel[l]+1;i<bel[r];++i) add[i]+=x;
}
else
{
cin>>y>>k;int ans=0,ANS=0;
if(bel[l]==bel[r])
{
for(register int i=l;i<=r;++i)
if(a[i]+add[bel[l]]<=y) ++ans;
}
else
{
for(register int i=l;i<=e[bel[l]];++i)
if(a[i]+add[bel[l]]<=y) ++ans;
for(register int i=b[bel[r]];i<=r;++i)
if(a[i]+add[bel[r]]<=y) ++ans;
for(register int i=bel[l]+1;i<bel[r];++i)
ans+=upper_bound(p+b[i],p+e[i]+1,y-add[i])-(p+b[i]);
}
++cnt;mo::p[cnt]={ans,min(ans,k),cnt};
}
}
mo::work();return 0;
}
考场想不到莫队求 \(S(n,m)\),让我对莫队有更深理解了。
T2 吃树(eat)
我是\(\huge\text{常数大}\)师。
与题解做法不同。当联通块大小 \(k\) 确定,对于一个点 \(x\),它每个儿子的子树中剩下未归到连通块中的点都将与 \(x\) 同属一个连通块。具体的,包含 \(x\) 的联通块大小一定大于等于 \(1+\sum\limits_{y\in son(x)} (siz_y \bmod k)\),所以若存在一个 \(x\) 使这个数大于 \(k\) 则不合法。
则先 dfs
一遍,然后枚举 \(k,k\mid n\),\(O(n)\) 判断是否合法。没必要递归写,会被卡常。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
const int MAXN=1e6+10;
int n,to[MAXN<<1],nxt[MAXN<<1],head[MAXN],cnt,siz[MAXN],fa[MAXN],ans;
inline void add(int x,int y)
{
to[++cnt]=y,nxt[cnt]=head[x];
head[x]=cnt;return ;
}
void dfs(int x)
{
siz[x]=1;
for(register int i=head[x];i;i=nxt[i])
{
int y=to[i];if(y==fa[x]) continue;
fa[y]=x,dfs(y);siz[x]+=siz[y];
}
return ;
}
int main()
{
#ifdef ONLINE_JUDGE
freopen("eat.in","r",stdin);
freopen("eat.out","w",stdout);
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(0);
#endif
cin>>n;
for(register int i=1;i<n;++i)
{
int x,y;cin>>x>>y;
add(x,y),add(y,x);
}
dfs(1);
for(register int k=1;k<=n;++k)
{
if(!(n%k))
{
for(register int x=1;x<=n;++x)
{
int sum=1;
for(register int i=head[x];i;i=nxt[i])
{
int y=to[i];if(y==fa[x]) continue;
sum+=siz[y]%k;
}
if(sum>k) break;
if(x==n) ++ans;
}
}
}
cout<<ans<<'\n';return 0;
}
赛时写递归主要是担心 \(y\) 不合法那么 \(y\) 的子树中未归到连通块中的点不等于 \(siz_y\bmod k\),所以每次重新 dfs
了,后来发现多此一举,只要存在一个不合法结果都是一样的。
T3 飞翔的胖鸟(fly)
whk 题,现学导数。一篇导数的博客。
求 \(\min\limits_{\theta\in (0,\frac{\Pi}{2}]} f(\theta)=\dfrac{ah}{\sin\theta} + b\theta\)。直接求导。
\[f'(\theta)=b-\dfrac{ah \cos \theta}{\sin^2 \theta} \]为避免精度问题设 \(x=\cos x\),由 \(\sin^2 x+\cos ^2 x=1\),将 \(\sin^2 x\) 替换为 \(1-x^2\),则:
\[f'(x)=b-\dfrac{ahx}{1-x^2}=b-x^2b-ahx=bx^2+ahx-b \]求极值,所以使 \(f'(x)=0\)。特判 \(b=0\),解二元一次方程,\(\theta=\arccos x\),将 \(\theta\) 带回函数求出答案。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<iomanip>
using namespace std;
const int MOD=19260817;
int typ,T,h,a,b,H,A,B;long long ans,P=1;
inline void Read(){
long long x=1ll*h*H,y=1ll*a*A,z=1ll*b*B;
h=(H^(y+z))%1000+1;
a=(A^(x+z))%1000+1;
b=(B^(x+y))%100000+1;
}
inline long double f(long double th){return (long double)a*h/sin(th)+b*th;}
int main()
{
#ifdef ONLINE_JUDGE
freopen("fly.in","r",stdin);
freopen("fly.out","w",stdout);
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(0);
#endif
cin>>typ>>T;
if(typ) cin>>h>>a>>b>>H>>A>>B;
for(register int i=1;i<=T;++i)
{
if(!typ) cin>>h>>a>>b;
else Read();P=P*11514%MOD;
long long A=b,B=a*h,C=-b;
double x=A?(sqrt(B*B-4*A*C)-B)/(A<<1):0;
if(!typ) cout<<fixed<<setprecision(4)<<f(acos(x))<<'\n';
else
{
long long now=floor(f(acos(x))*10000);
ans+=now%MOD*P%MOD;
}
}
if(typ) cout<<ans%MOD<<'\n';
return 0;
}
不会导数是真的。
T4 漂亮轰炸(bomb)
没改完,先咕。
标签:bel,NOIP,int,long,MAXN,联测,theta,include From: https://www.cnblogs.com/int-R/p/NOIP-A-5.html