21牛客多校第二场

A

\(b_i=|a_i-a_{i+1}|\) 对于一段区间 \([l,r]\),若这一段内的数构成等差数列,则需\(max\{a_l,\dots ,a_r\}-min\{a_l,\dots ,a_r\}=gcd(b_l,\dots ,b_{r-1})\cdot (r-l)\)

当无法构成等差数列时,显然有\(max-min\ge gcd\cdot(r-l)\)

考虑枚举\(r\),用线段树维护\(max\{a_l,\dots ,a_r\}-min\{a_l,\dots ,a_r\}+gcd(b_l,\dots ,b_{r-1})\cdot l\)

对于\(max,min\)值的变化可以用单调栈维护,每次修改时线段树修改一段区间答案即可

因为\(gcd\)不同的区间个数至多\(log\)个,开一个数组统计区间的端点,每次更新并合并

\(gcd\)出现变化,可以暴力修改区间内每个点的点值,因为每个点至多被修改\(log\)

由于存在上述不等关系因此\(gcd(b_l,\dots ,b_{r-1})\cdot r\)一定是该区间内线段树上的最小值,同时需要求的是满足等式的\(l\)个数

因此在线段树上维护\(min,cnt_{min}\)表示线段树节点内最小值与最小值个数

对每个右端点,在相同\(gcd\)的区间内查询与\(gcd\cdot r\)相等的数量即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,g[MAXN],cnt[MAXN<<2];
ll mn[MAXN<<2],tag[MAXN<<2],ans;
void build(int k,int l,int r)
{
	mn[k]=tag[k]=0,cnt[k]=1;if(l==r) return ;int mid=l+r>>1;
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
inline void pshd(int k)
{
	tag[k<<1]+=tag[k],tag[k<<1|1]+=tag[k];
	mn[k<<1]+=tag[k],mn[k<<1|1]+=tag[k],tag[k]=0;
}
inline void upd(int k)
{
	mn[k]=min(mn[k<<1],mn[k<<1|1]);
	cnt[k]=cnt[k<<1]*(mn[k<<1]==mn[k])+cnt[k<<1|1]*(mn[k<<1|1]==mn[k]);
}
void mdf(int k,int l,int r,int a,int b,ll w)
{
	if(a<=l&&r<=b) {tag[k]+=w,mn[k]+=w;return ;}
	int mid=l+r>>1;pshd(k);
	if(a<=mid) mdf(k<<1,l,mid,a,b,w);
	if(b>mid) mdf(k<<1|1,mid+1,r,a,b,w);
	upd(k);
}
int query(int k,int l,int r,int a,int b,ll w)
{
	if(a<=l&&r<=b) return (mn[k]==w)*cnt[k];
	int mid=l+r>>1,res=0;pshd(k);
	if(a<=mid) res=query(k<<1,l,mid,a,b,w);
	if(b>mid) res+=query(k<<1|1,mid+1,r,a,b,w);
	return res;
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int s1[MAXN],s2[MAXN],t1,t2,a[MAXN],tp,w[MAXN];
int main()
{
	int x,nw;rep(T,1,read())
	{
		n=read();build(1,1,n);t1=t2=tp=0;ans=0LL;
		rep(i,1,n)
		{
			g[i]=read();
			for(;t1&&g[s1[t1]]<g[i];t1--)
				mdf(1,1,n,s1[t1-1]+1,s1[t1],g[i]-g[s1[t1]]);
			for(;t2&&g[s2[t2]]>g[i];t2--)
				mdf(1,1,n,s2[t2-1]+1,s2[t2],-g[i]+g[s2[t2]]);
			s1[++t1]=i,s2[++t2]=i;
			if(i==1) continue;
			x=abs(g[i]-g[i-1]);a[++tp]=i-1,w[tp]=x;
			mdf(1,1,n,i-1,i-1,1LL*(i-1)*x);
			dwn(i,tp-1,1) 
			{
				nw=gcd(w[i+1],w[i]);if(nw==w[i]) continue;
				rep(j,a[i-1]+1,a[i])
					mdf(1,1,n,j,j,1LL*j*(nw-w[i]));
				w[i]=nw;
			}
			x=0;rep(i,1,tp) if(w[i]!=w[i+1]||i==tp)
				a[++x]=a[i],w[x]=w[i];
			tp=x;rep(j,1,tp) ans+=query(1,1,n,a[j-1]+1,a[j],1LL*i*w[j]);
		}
		printf("%lld\n",ans+n);
	}
}

B

首先易知每一列进行\(k\)次操作的方案数是\(\prod\limits_{i=n-k+1}^n 2i=2^k\frac{n!}{(n-k)!}\)

则第一问中:

\[\begin{aligned} f_k=&\sum\limits_{i+j=k} 2^i\frac{n!}{(n-i)!}\cdot 2^j\frac{m!}{(m-j)!}\binom{i+j}{i} \\=&2^k\sum\limits_{i+j=k} \frac{n!}{(n-i)!}\cdot \frac{m!}{(m-j)!}\frac{k!}{i!\ j!} \\=&2^k k!\sum\limits_{i+j=k} \frac{n!}{(n-i)!i!}\cdot \frac{m!}{(m-j)!j!} \\=&2^k k!\sum\limits_{i+j=k} \binom{n}{i}\cdot \binom{m}{j} \\=&2^k k!\binom{n+m}{k} \end{aligned} \]

预处理\(2^i\ ,\ i!\ , \ \frac{1}{i!}\)即可\(O(1)\)求出\(f_i\)

其中最后一步为范德蒙德卷积,组合意义也很显然

对于第二问,则\(f_i\)没有了后面的组合数系数

\[\begin{aligned} f_k=&\sum\limits_{i+j=k} 2^i\frac{n!}{(n-i)!}\cdot 2^j\frac{m!}{(m-j)!}\\ =&2^k\ n!\ m!\sum\limits_{i+j=k} \frac{1}{(n-i)!}\cdot \frac{1}{(m-j)!}\\ =&2^k\ n!\ m!\sum\limits_{i+j=k} \frac{(n+m-i-j)!}{(n-i)!(m-j)!}\cdot \frac{1}{(n+m-i-j)!}\\ =&\frac{2^k\ n!\ m!}{(n+m-k)!}\sum\limits_{i+j=k} \frac{(n+m-k)!}{(n-i)!(m-j)!}\\ =&\frac{2^k\ n!\ m!}{(n+m-k)!}\sum\limits_{i+j=k} \binom{n+m-k}{n-i}\\ =&\frac{2^k\ n!\ m!}{(n+m-k)!}\sum\limits_{i=0}^k \binom{n+m-k}{n-i}\\ \end{aligned} \]

即每次求一行中一段连续组合数的和即\(n+m-k\)行的\(n-k\)项到\(n\)

显然会出现正常组合数表之外的一些值,全赋值为\(0\)后依据数表\(\binom{n}{m}=\binom{n-1}{m}+\binom{n-1}{m-1}\) 的规律递推计算

每一行的值等于上一行的答案乘二并减去边界值,即\(ans_i=2ans_{i+1}-\binom{n+m-i-1}{n}-\binom{n+m-i-1}{n-i-1}\)

则两小问的答案均可\(O(1)\)算出

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 10010010
#define MOD 1000000009
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,fac[MAXN],ifac[MAXN],pw2[MAXN],ans;
int f[MAXN],g[MAXN];
int q_pow(int bas,int t,int res=1)
{
    for(;t;t>>=1,bas=mul(bas,bas)) if(t&1) res=mul(res,bas);return res;
}
#define inv(x) q_pow(x,MOD-2)
inline int C(int n,int m)
{
	if(m<0||n<m) return 0;
	return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
int main()
{
	n=read()-2,m=read()-2;
	fac[0]=ifac[0]=pw2[0]=1;
	rep(i,1,n+m) fac[i]=mul(fac[i-1],i),pw2[i]=mul(pw2[i-1],2);
	ifac[n+m]=inv(fac[n+m]);
	dwn(i,n+m-1,1) ifac[i]=mul(ifac[i+1],i+1);
	rep(i,0,n+m) f[i]=mul(mul(pw2[i],fac[i]),C(n+m,i));
	rep(i,0,n+m) ans^=f[i];printf("%d ",ans);ans=0;
	g[n+m]=1;
	dwn(i,n+m-1,0) g[i]=mns(mul(g[i+1],2),pls(C(n+m-i-1,n-i-1),C(n+m-i-1,n)));
	rep(i,0,n+m) f[i]=mul(mul(mul(pw2[i],ifac[n+m-i]),mul(fac[n],fac[m])),g[i]);
	rep(i,0,n+m) ans^=f[i];printf("%d\n",ans);
}

C

签到题,题意转化为两个人轮流选一条边,最终构成一棵树,显然最终胜负只与点数奇偶性有关

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
int main()
{
	n=read(),m=read();
	puts(((n*m)&1)?"NO":"YES");
}

D

签到题,按题意模拟即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct data{int x,y,w,eq,mx;}a,b;
void mem(data* t)
{
	if((*t).x>(*t).y) swap((*t).x,(*t).y);
	if((*t).x==2&&(*t).y==8) (*t).mx=1;else (*t).mx=0;
	if((*t).x==(*t).y) (*t).eq=1;else (*t).eq=0;
	(*t).w=((*t).x+(*t).y)%10;
}
int main()
{
	rep(T,1,read())
	{
		a.x=read(),a.y=read(),b.x=read(),b.y=read();
		mem(&a);mem(&b);
		if(a.mx!=b.mx) {puts(a.mx?"first":"second");continue;}
		else if(a.mx==1) {puts("tie");continue;}
		if(a.eq!=b.eq) {puts(a.eq?"first":"second");continue;}
		else if(a.eq)
		{
			if(a.x!=b.x) puts(a.x>b.x?"first":"second");
			else puts("tie");
			continue;
		}
		if(a.w!=b.w) {puts(a.w>b.w?"first":"second");continue;}
		else 
		{
			if(a.y!=b.y) puts(a.y>b.y?"first":"second");
			else puts("tie");
		}
	}
}

E

点分治,先将所有询问放到起点上去 先不考虑每次询问被\(ban\)的节点

对于每个分治重心,需要处理的是:从根走到每个点最少需要多少权值、需要多少权值才能从每个点走到根、每个点到根的花费

是一个比较容易的点分治,对每个查询计算\(q_x\)后,只需要统计有多少\(x\le q_x\)满足条件即可

但由于每次询问都有一个点阻隔了路径,考虑把容斥放到点分治的\(calc\)里去

若这个禁止点位于查询起点到分治重心的路径即此次分治没有合法路径

否则对于查询,除了原本的子树容斥以外,还需要减去禁止点子树内的答案

如此将每个查询拆成若干干个子树内查询,在\(calc\)时需要对一些询问计算这个子树内有多少\(x\le q_x\),需要一个支持单点修改和查询前缀和的数据结构,可以将权值离散化后使用树状数组

注意结束分治时的标记和查询清空,以及起始点位于分治重心时的特殊情况

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define pli pair<ll,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,Q,in[MAXN],out[MAXN],bl[MAXN],dfn,hsh[MAXN],w[MAXN],c[MAXN];
int fst[MAXN],nxt[MAXN<<1],to[MAXN<<1],val[MAXN<<1],cnt;
int mx[MAXN],sz[MAXN],Sum,Rt,Mx,vis[MAXN],ans[MAXN],tot;
ll dis[MAXN],upw[MAXN],dnw[MAXN],G[MAXN];
struct Ask{int id,x;ll d;};vector<Ask> vec[MAXN];
struct Query{int w,val,id;};vector<Query> q[MAXN];
void add(int u,int v,int w){nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v,val[cnt]=w;}
void mdf(int x){for(;x<=tot;x+=x&-x) c[x]++;}
int query(int x,int res=0){for(;x;x-=x&-x) res+=c[x];return res;}
void getrt(int x,int pa)
{
	mx[x]=0,sz[x]=1;ren if(to[i]^pa&&!vis[to[i]])
		getrt(to[i],x),mx[x]=max(mx[x],sz[to[i]]),sz[x]+=sz[to[i]];
	mx[x]=max(mx[x],Sum-mx[x]);if(mx[x]<Mx) Mx=mx[x],Rt=x;
}
void dfs(int x,int pa,ll Dis)
{
	Dis+=w[x],dis[x]=Dis,in[x]=++dfn,hsh[dfn]=x;
	//cout<<"dfs: "<<x<<" "<<pa<<" "<<Dis<<" ";
	//cout<<upw[x]<<" "<<dnw[x]<<" "<<bl[x]<<endl;
	ren if(to[i]^pa&&!vis[to[i]])
	{
		bl[to[i]]=!pa?to[i]:bl[x];
		dnw[to[i]]=min(dnw[x],Dis-val[i]);
		upw[to[i]]=min(0LL,upw[x]+w[to[i]]-val[i]);
		dfs(to[i],x,Dis-val[i]);
	}
	out[x]=dfn;
}
void calc(int x,int pa)
{
	//cout<<"calc: "<<x<<" "<<pa<<endl;
	//for(auto i:q[x]) cout<<i.id<<" "<<i.val<<" "<<i.w<<endl;
	for(auto i:q[x]) ans[i.id]-=i.w*query(i.val);
	//cout<<x<<":begin:   ";rep(i,1,Q) cout<<ans[i]<<" ";puts("");
	mdf(dnw[x]);ren if(to[i]^pa&&!vis[to[i]]) calc(to[i],x);
	for(auto i:q[x]) ans[i.id]+=i.w*query(i.val);in[x]=n+1;q[x].clear();
	//cout<<x<<":end:   ";rep(i,1,Q) cout<<ans[i]<<" ";puts("");
}
void div(int x)
{
	//cout<<"div: "<<x<<endl;
	vis[x]=1;dfn=0;upw[x]=dnw[x]=0;dfs(x,0,0);
	rep(i,1,dfn) G[i]=-dnw[hsh[i]];sort(G+1,G+dfn+1);
	tot=unique(G+1,G+dfn+1)-G-1;
	rep(i,1,dfn) dnw[hsh[i]]=lower_bound(G+1,G+tot+1,-dnw[hsh[i]])-G;
	//rep(i,1,dfn) cout<<hsh[i]<<" "<<dnw[hsh[i]]<<endl;
	rep(i,1,tot+5) c[i]=0;
	rep(i,1,dfn)
	{
		int u=hsh[i];ll v;for(auto j:vec[u]) if(upw[u]+j.d>=0)
		{
			if(in[j.x]<=in[u]&&out[j.x]>=out[u]) continue;
			v=dis[u]+j.d-w[x],v=upper_bound(G+1,G+tot+1,v)-G-1;
			//cout<<u<<" "<<i<<" "<<j.d<<" "<<j.x<<endl;
			//cout<<dis[u]+j.d-w[x]<<" "<<v<<endl;
			q[x].pb({1,(int)v,j.id});
			if(u!=x)
			{
				q[bl[u]].pb({-1,(int)v,j.id});
				if(in[j.x]!=n+1&&(in[j.x]<in[bl[u]]||in[j.x]>out[bl[u]]))
					q[j.x].pb({-1,(int)v,j.id});
			}
			else if(in[j.x]!=n+1) q[j.x].pb({-1,(int)v,j.id});
		}
	}
	calc(x,0);
	ren if(!vis[to[i]])
		{Sum=sz[to[i]],Mx=n+1;getrt(to[i],0);div(Rt);}
}
int main()
{
	n=read(),Q=read();int a,b;ll c;
	rep(i,2,n) {a=read(),b=read(),c=read();add(a,b,c);add(b,a,c);}
	rep(i,1,n) w[i]=read(),in[i]=n+1;
	rep(i,1,Q) {a=read(),c=read(),b=read();vec[a].pb({i,b,c});}
	Sum=n,Mx=n+1;getrt(1,0);div(Rt);
	rep(i,1,Q) printf("%d\n",ans[i]);
}

F

在一个平面内 到两个定点距离之比为定值的图形即为阿氏圆,空间内为一球

\((x-x_1)^2+(y-y_1)^2+(z-z_1)^2=k[(x-x_2)^2+(y-y_2)^2+(z-z_2)^2]\)即可推出球的方程

因为式子中\(x,y,z\)形式完全相同 算一个即可

化简\(x\)的式子可得\((k^2-1)x^2-2(k^2x_2-x_1)x+(kx_2)^2-x_1^2\)

\(\lgroup x-\frac{k^2x_2-x_1}{k^2-1}\rgroup^2=(\frac{k^2x_2-x_1}{k^2-1})^2+\frac{x_1^2-(kx_2)^2}{k^2-1}\)

对于\(y,z\)同理 可得到两个球的球心和半径

注意先判断相离和包含的情况

然后可以将球投影至平面 求出交点位置之后用旋转体积分公式求出即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const ld pi=acos(-1.0);
const ld eps=1e-5;
struct point
{
	ld x,y,z;
	void init(){x=read(),y=read(),z=read();}
}a,b,c,d,c1,c2;
ld k1,k2,r1,r2,l,p,ans;
inline ld sqr(ld x){return x*x;}
ld dis(point a,point b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y)+sqr(a.z-b.z));}
point solve(point a,point b,ld k,ld &r)
{
	ld x,y,z;r=0;
	x=(a.x-k*k*b.x)/(k*k-1);
	r+=sqr(x)+(sqr(a.x)-sqr(k*b.x))/(k*k-1);
	y=(a.y-k*k*b.y)/(k*k-1);
	r+=sqr(y)+(sqr(a.y)-sqr(k*b.y))/(k*k-1);
	z=(a.z-k*k*b.z)/(k*k-1);
	r+=sqr(z)+(sqr(a.z)-sqr(k*b.z))/(k*k-1);
	r=sqrt(r);//cout<<x<<" "<<y<<" "<<z<<" "<<r<<endl;
	return {-x,-y,-z};
}
ld calc(ld x,ld r)
{
	return pi*(r*r/3.0*r*2-r*r*x+x*x/3.0*x);
}
int main()
{
	rep(T,1,read())
	{
		a.init();b.init();c.init();d.init();
		k1=read(),k2=read();
		c1=solve(a,b,k1,r1);
		c2=solve(c,d,k2,r2);
		l=dis(c1,c2);
		if(r1+r2-l<=eps) {puts("0");continue;}
		if(r1>r2) swap(r1,r2);
		if(r2-r1-l>=-eps) {printf("%.5Lf\n",pi*4/3*r1*sqr(r1));continue;}
		p=(sqr(r1)-sqr(r2)+sqr(l))/(2*l);
		//cout<<r1<<" "<<r2<<" "<<l<<" "<<p<<endl;
		ans=calc(p,r1);
		ans+=calc(l-p,r2);
		printf("%.5Lf\n",ans);
	}
}

G

首先考虑两个线段出现包含的情况,设\(a\subseteq b\),则\(b\)一定是单独作为一组或属于\(a\)所在的组

因为若最终答案内\(b\)\(a\)所属不同组且\(b\)组内元素大于1,则将\(b\)从所在组移动到\(a\)所在组后\(b\)组答案只可能变大,而\(a\)组答案不可能变小,总答案也会更优

在这个结论之上,先将所有存在包含关系的大区间取出

这样剩余区间不存在包含关系,即按左端点排序后右端点也是单调的,每次显然选取的连续一段区间

\(dp(i,j)\)表示前\(i\)个线段分了\(j\)组,\(dp(i,j)=max\{dp(k,j-1)+r_{k+1}-l_i\},k<i\)

因为\(r\)是单增的且\(dp(k,j-1)+r_{k+1}\)仅与\(k\)有关,对每一个\(j\)可以用单调队列优化

记之前取出大区间前\(k\)长的区间长度和为\(sum_k\),枚举要选的单独区间个数 最终\(ans=max\{dp(n,m-k)+sum_k\}\)

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,k,dp[5050][5050],m,len[5050],q[5050],hd,tl,tot,ans,sum[5050];
pii g[5050];
bool cmp(pii a,pii b) {return a.se<b.se||(a.se==b.se&&a.fi>b.fi);}
int main()
{
	n=read(),k=read();rep(i,1,n) g[i].fi=read(),g[i].se=read();
	sort(g+1,g+n+1,cmp);int l=-1;
	rep(i,1,n)
		if(g[i].fi<=l) len[++tot]=g[i].se-g[i].fi;
		else l=g[i].fi,g[++m]=g[i];
	Fill(dp,-127);n=m;dp[0][0]=0;
	rep(j,1,k)
	{
		hd=0,tl=-1;if(j==1) q[++tl]=0;
		rep(i,1,n)
		{
			while(g[q[hd]+1].se<g[i].fi&&hd<=tl) hd++;
			if(hd<=tl) dp[i][j]=dp[q[hd]][j-1]+g[q[hd]+1].se-g[i].fi;
			//cout<<i<<" "<<j<<" "<<hd<<" "<<tl<<" "<<dp[i][j]<<endl;
			while(hd<=tl&&g[q[tl]+1].se+dp[q[tl]][j-1]<=dp[i][j-1]+g[i+1].se) tl--;
			//cout<<i<<" "<<j<<" "<<hd<<" "<<tl<<endl;
			q[++tl]=i;
		}
	}
	sort(len+1,len+tot+1);rep(i,1,tot) sum[i]=sum[i-1]+len[tot-i+1];
	rep(i,0,min(k,tot)) ans=max(ans,dp[n][k-i]+sum[i]);
	printf("%d\n",ans);
}

H

结论题+分治\(ntt\) 弃了

I

直接按照移动的字典序爆搜\(bfs\),记录一下路径最后回溯即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 2001000
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define mp(a,b) make_pair(a,b)
#define x first
#define y second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int n=20,m=20,dx[4]={1,0,0,-1},dy[4]={0,-1,1,0};
const char Op[5]="DLRU";
int dis[440][440],hd,tl,op[440][440];
char s[2][25][25];
pair<pii,pii> q[MAXN],frm[440][440];
inline int hsh(pii i){return (i.x-1)*n+i.y;}
pii move(pii a,int t,int k)
{
	pii b={a.x+dx[t],a.y+dy[t]};
	if(b.x<1||b.x>n||b.y<1||b.y>m) return a;
	if(s[k][b.x][b.y]=='#') return a;
	return b;
}
int bfs()
{
	q[hd=tl=1]={{20,20},{20,1}};dis[hsh({20,20})][hsh({20,1})]=1;
	pii a,b,c,d;int st;while(hd<=tl)
	{
		a=q[hd].x,b=q[hd++].y,st=dis[hsh(a)][hsh(b)];
		rep(i,0,3)
		{
			c=move(a,i,0);
			if(i==1||i==2) d=move(b,3-i,1);
			else d=move(b,i,1);
			if(!dis[hsh(c)][hsh(d)])
				q[++tl]={c,d},dis[hsh(c)][hsh(d)]=st+1,
				frm[hsh(c)][hsh(d)]={a,b},op[hsh(c)][hsh(d)]=i;
			if(c==mp(1,20)&&d==mp(1,1)) return dis[20][1]-1;
		}
	}
}
void Re(pair<pii,pii> a)
{
	s[0][a.x.x][a.x.y]='A',s[1][a.y.x][a.y.y]='A';
	if(a.x==mp(20,20)&&a.y==mp(20,1)) return ;
	Re(frm[hsh(a.x)][hsh(a.y)]);
	printf("%c",Op[op[hsh(a.x)][hsh(a.y)]]);
}
int main()
{
	rep(i,1,n)
	{
		rep(j,1,m) s[0][i][j]=getchar();getchar();
		rep(j,1,m) s[1][i][j]=getchar();getchar();
	}
	printf("%d\n",bfs());
	Re({{1,20},{1,1}});puts("");
	rep(i,1,n)
	{
		rep(j,1,m) putchar(s[0][i][j]);putchar(' ');
		rep(j,1,m) putchar(s[1][i][j]);putchar('\n');
	}
}

J

显然需要枚举质因数进行计算,即\(ans=\prod p^{G(p)}\)其中\(G(p)\)表示质数\(p\)的贡献

\(F(p^c)\)表示满足\(p^c=gcd\)的集合的个数,则\(G(p)=\sum\limits_{i=1}^{\infty}\binom{F(p^i)}{m}\cdot i\)

但这种意义下\(F\)并不好统计,考虑满足统计\(p^c|gcd\)的集合个数,则\(G(p)=\sum\limits_{i=1}^{\infty}\binom{F(p^i)}{m}\) 意义显然

因为\(G(p)\)在指数上而\(MOD\)不确定,\(gcd(MOD,p)\)不一定为一

使用扩展欧拉定理,先求出\(\varphi(MOD)\),后在递推计算组合数及\(G(p)\)的时候做一下处理

若这些值不大于\(\varphi\)则无需处理,大于\(\varphi\)时保留为\(\varphi+x\%\varphi\)的形式

每次计算出\(G(p)\)后快速幂即可(快速幂可能会爆\(long\ long\),需要使用\(\_\_int128\)

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 80000
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MN=1e7+5;
int n,k,ntp[MN],tot,p[MN/7],w[MAXN+10];ll P,phi,c[MAXN][35],mod,ans;
void mem(int n)
{
	rep(i,2,n)
	{
		if(!ntp[i]) p[++tot]=i;
		rep(j,1,tot) if(1LL*p[j]*i>n) break;
			else {ntp[i*p[j]]=1;if(i%p[j]==0) break;}
	}
}
inline ll mul(ll a,ll b){return (__int128)a*b%mod;}
ll qp(ll x,ll t,ll res=1)
{
	for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);
	return res;
}
int main()
{
	mem(1e7);rep(T,1,read())
	{
		n=read(),k=read(),phi=P=mod=read();ans=1;
		Fill(w,0);
		rep(i,1,n) w[read()]++;
		rep(i,1,tot) if(p[i]*p[i]>=P) break;
			else if(P%p[i]==0)
			{
				phi=phi/p[i]*(p[i]-1);
				while(P%p[i]==0) P/=p[i];
			}
		if(P>1) phi=phi/P*(P-1);
		rep(i,0,n)
		{
			c[i][0]=1;rep(j,1,min(k,i))
			{
				c[i][j]=c[i-1][j]+c[i-1][j-1];
				while(c[i][j]>=phi+phi) c[i][j]-=phi;
			}
		}
		rep(i,2,MAXN) for(int j=i+i;j<=MAXN;j+=i) w[i]+=w[j];
		rep(i,1,tot) if(p[i]>MAXN) break;
			else
			{
				ll res=0;for(ll j=p[i];j<=MAXN;j*=p[i])
					{res+=c[w[j]][k];while(res>=phi+phi) res-=phi;}
				if(res) ans=mul(ans,qp(p[i],res));
			}
		printf("%lld\n",ans);
	}
}

K

对于没有给定值的位置,容易发现令他们默认不弹出不影响答案

模拟维护一个栈,每次若长度\(+1\)则有该数\(>\)前一个数

若减小,则找到当前栈内的临界点,当前这个数应该在找到的两个数之间

如此模拟可以得到一些拓扑关系,连边后拓扑排序即可得到答案

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 1001001
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,k,b[MAXN],g[MAXN],t[MAXN],p[MAXN],ind[MAXN],q[MAXN],l=1,r,tot;
vector<int> G[MAXN];
int main()
{
	n=read(),k=read();int x;
	rep(i,1,k) x=read(),b[x]=read();
	rep(i,1,n) if(!b[i]) G[i-1].pb(i),b[i]=b[i-1]+1,p[b[i]]=i,ind[i]++;
		else 
		{
			if(b[i]>b[i-1]+1) return puts("-1"),0;
			G[p[b[i]-1]].pb(i);ind[i]++;
			if(p[b[i]]) {ind[p[b[i]]]++;G[i].pb(p[b[i]]);}
			p[b[i]]=i;
		}
	rep(i,0,n) if(!ind[i]) q[++r]=i;
	while(l<=r)
	{
		x=q[l++],g[x]=tot++;
		for(auto v:G[x]) if(!(--ind[v])) q[++r]=v;
	}
	rep(i,1,n) printf("%d%c",g[i],i==n?'\n':' ');
}

L

发现一种很妙的做法

记一个操作为\(\{w,x,t\}\),表示点\(x\)在时间\(t\)权值变为了\(w\)

因为正序维护不太好做,同时每个点的权值都不会超过\(10^4\),将所有操作离线下来之后按照\(w\)倒序排序

这样每个点更改的时候是他成为冠军时间的结束而不是开始

记录对于\(w\ge w_i\)的所有操作中,\(x_i\)周围所有点更改操作的最小时间\(t_x\),则\(x_i\)点成为冠军的时段为\([t_i,t_x)\)\(t_x\)同时还需要与\(x_i\)点上次更改的时间取\(min\),否则会重复计算

现在考虑如何维护\(t_x\),采用根号分治

对于\(deg<\sqrt{n}\)的点,每次可以暴力查询,单次复杂度不超过\(O(\sqrt{n})\)

对于\(deg>\sqrt{n}\)的点,维护一个\(mn\)数组,每个它周围点有更新的时候都更新它,这样可以做到\(O(1)\)查询,又由于这些点的个数很少,因此修改的复杂度每次也不超过\(O(\sqrt{n})\)

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 200100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,q,sz,nxt[MAXN<<1],fst[MAXN],to[MAXN<<1],deg[MAXN],cnt,w[MAXN];
int mn[MAXN],las[MAXN],now[MAXN],ans[MAXN];
vector<int> G[MAXN];
vector<pii> g[10010];
void add(int u,int v){nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v,deg[u]++;}
int main()
{
	n=read(),m=read(),q=read(),sz=sqrt(n);int a,b;
	rep(i,1,m) a=read(),b=read(),add(a,b),add(b,a);
	rep(i,1,q) a=read(),w[a]+=read(),g[w[a]].pb({a,i});
	rep(x,1,n) ren if(deg[to[i]]>sz) G[x].pb(to[i]);
	rep(i,1,n) now[i]=las[i]=mn[i]=q;
	pii p;dwn(i,10000,1)
	{
		#define x p.first
		#define t p.second
		for(auto p:g[i])
		{
			for(auto v:G[x]) mn[v]=min(mn[v],t);
			las[x]=now[x],now[x]=t;
		}
		for(auto p:g[i])
			if(deg[x]<=sz)
			{
				int tmp=las[x];ren tmp=min(tmp,now[to[i]]);
				ans[x]+=max(0,tmp-t);
			}
			else ans[x]+=max(0,min(mn[x],las[x])-t);
	}
	rep(i,1,n) printf("%d\n",ans[i]);
}
posted @ 2021-07-30 17:30  jack_yyc  阅读(59)  评论(0编辑  收藏  举报