【前言】
这是打的第二场,rk39,但是AB这两个比较简单的题都没做emm,大概还是磨合的不够。然后感觉对于阈值类的东西还不是很敏感,应该看到不太好做就直接去想这种阈值的。校内3/9(然后就开启了常年校内第三的生活,前面一个银川冠军,一个大三队)

A. Arithmetic Progression

【题意】

给定一个序列aia_iai​,求满足排序后是等差数列的区间个数。

n≤105n\leq 10^5n≤105

【思路】

首先需要一个快速的判定方法:

对于序列bib_ibi​,若其排序后为等差数列,则必然有公差d=gcd(b2−b1,b3−b2,...)d=gcd(b_2-b_1,b_3-b_2,...)d=gcd(b2​−b1​,b3​−b2​,...)

于是问题转化为统计区间(l,r)(l,r)(l,r)满足:
max⁡(a[l..r])−min⁡(a[l..r])=(r−l)⋅∣gcd(...)∣\max(a[l..r])-\min(a[l..r])=(r-l) \cdot |gcd(...)| max(a[l..r])−min(a[l..r])=(r−l)⋅∣gcd(...)∣
枚举rrr,可以在结合单调栈在线段树上维护max⁡−min⁡\max-\minmax−min的值,处理区间修改操作,对于不同的l,gcdl,gcdl,gcd的不同分段只有log⁡a\log aloga段,可以在lll每次gcdgcdgcd改变时在线段树上做单点修改

又max⁡(l,r)−min⁡(l,r)−(r−l)⋅∣gcd(...)∣≥0\max(l,r)-\min(l,r)-(r-l) \cdot |gcd(...)|\geq 0max(l,r)−min(l,r)−(r−l)⋅∣gcd(...)∣≥0,故可以统计线段树上的最小值及其出现的次数,就能得到区间内该表达式为000的个数

复杂度O(nlog⁡2n)O(n\log^2 n)O(nlog2n)

【参考代码】

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
const int maxn=3e5+5;
int n,m;
ll mn[maxn<<2],cnt[maxn<<2],tag[maxn<<2];
void add(int v,int ad){mn[v]+=ad;tag[v]+=ad;
}
#define ls (v<<1)
#define rs (v<<1|1)
#define mid ((l+r)>>1)
void push_down(int v){if(tag[v]){add(ls,tag[v]);add(rs,tag[v]);tag[v]=0;}
}
void push_up(int v){mn[v]=min(mn[ls],mn[rs]);cnt[v]=mn[ls]==mn[rs]?cnt[ls]+cnt[rs]:(mn[ls]<mn[rs]?cnt[ls]:cnt[rs]);
}
void modify(int v,int l,int r,int al,int ar,ll ad){if(al<=l&&ar>=r) return add(v,ad);push_down(v);if(al<=mid) modify(ls,l,mid,al,ar,ad);if(ar>mid) modify(rs,mid+1,r,al,ar,ad);push_up(v);
}
void build(int v,int l,int r){mn[v]=tag[v]=0;cnt[v]=1;if(l==r) {return; }build(ls,l,mid);build(rs,mid+1,r);push_up(v);
}
ll query(int v,int l,int r,int al,int ar,ll z){if(al<=l&&ar>=r) return mn[v]==z?cnt[v]:0;push_down(v);ll ans=0;if(al<=mid) ans+=query(ls,l,mid,al,ar,z);if(ar>mid) ans+=query(rs,mid+1,r,al,ar,z);return ans;
}int a[maxn];
int b[maxn];
int s1[maxn],s2[maxn];
int c[maxn],w[maxn];
int t1,t2,tp;
signed main(){dwn(_,yh(),1){n=yh();rep(i,1,n){a[i]=yh();b[i]=abs(a[i]-a[i-1]);}ll ans=0;build(1,1,n);t1=0;t2=0;tp=0;rep(i,1,n){for(;t1&&a[s1[t1]]<a[i];t1--)modify(1,1,n,s1[t1-1]+1,s1[t1],a[i]-a[s1[t1]]);for(;t2&&a[s2[t2]]>a[i];t2--)modify(1,1,n,s2[t2-1]+1,s2[t2],a[s2[t2]]-a[i]);s1[++t1]=i;s2[++t2]=i;if(i==1)continue;w[++tp]=b[i];c[tp]=i-1;modify(1,1,n,i-1,i-1,1ll*(i-1)*b[i]);dwn(i,tp-1,1){int nw=__gcd(w[i+1],w[i]);if(nw==w[i])continue;rep(j,c[i-1]+1,c[i])modify(1,1,n,j,j,1ll*j*(nw-w[i]));w[i]=nw;}int nt=0;rep(i,1,tp) if(w[i]!=w[i+1]||i==tp){c[++nt]=c[i];w[nt]=w[i];}tp=nt;rep(j,1,tp) ans+=query(1,1,n,c[j-1]+1,c[j],1ll*i*w[j]);}cout<<ans+n<<hvie;}return 0;
}

B. Cannon

【题意】

有一个2×101002\times 10^{100}2×10100的棋盘,其中第一行摆了aaa个炮,第二行摆了bbb个炮,求依次发生kkk个炮吃炮事件的方案数。

并且求考虑两行之间顺序以及不考虑两行之间顺序的两个问题的答案。

a,b≤5×106a,b\leq 5\times 10^6a,b≤5×106

【思路】

容易发现包含 nnn 个炮的一行吃一次的方案数为2(n−2)2(n-2)2(n−2),则nnn个炮操作 $m $次的方案数为2m(n−2)!(n−2−m)!2^m \frac{(n-2)!}{(n-2-m)!}2m(n−2−m)!(n−2)!​

设n=a−2,m=b−2n=a-2,m=b-2n=a−2,m=b−2 ,则两个问题分别是对于每个kkk求

  • 2k∑k!i!(k−i)!n!(n−i)!m!(m−(k−i))!2^k\sum \frac {k!}{i!(k-i)!}\frac {n!}{(n-i)!}\frac {m!}{(m-(k-i))!}2k∑i!(k−i)!k!​(n−i)!n!​(m−(k−i))!m!​
  • 2k∑n!(n−i)!m!(m−(k−i))!2^k\sum\frac {n!}{(n-i)!}\frac {m!}{(m-(k-i))!}2k∑(n−i)!n!​(m−(k−i))!m!​

对前一个问题,容易发现:
2k∑k!i!(k−i)!n!(n−i)!m!(m−(k−i))!=2k∑k!(ni)(mk−i)=2kk!(n+mk)\begin{aligned} &2^k\sum \frac {k!}{i!(k-i)!}\frac {n!}{(n-i)!}\frac {m!}{(m-(k-i))!}\\=&2^k\sum k!\binom{n}{i}\binom{m}{k-i}\\ =&2^kk!\binom{n+m}{k} \end{aligned} ==​2k∑i!(k−i)!k!​(n−i)!n!​(m−(k−i))!m!​2k∑k!(in​)(k−im​)2kk!(kn+m​)​
对于第二个问题,那么有:
2k∑n!(n−i)!m!(m−(k−i))!=2k∑n!m!(n+m−k)!(n+m−k)!(n−i)!(m−(k−i))!=2kn!m!(n+m−k)!∑n−kn(n+m−ki)\begin{aligned} &2^k\sum \frac {n!}{(n-i)!}\frac {m!}{(m-(k-i))!}\\=&2^k\sum \frac{n!m!}{(n+m-k)!}\frac {(n+m-k)!}{(n-i)!(m-(k-i))!}\\ =&2^k\frac {n!m!}{(n+m-k)!}\sum_{n-k}^n\binom{n+m-k}{i} \end{aligned} ==​2k∑(n−i)!n!​(m−(k−i))!m!​2k∑(n+m−k)!n!m!​(n−i)!(m−(k−i))!(n+m−k)!​2k(n+m−k)!n!m!​n−k∑n​(in+m−k​)​
这是一个组合数关于mmm的前缀和问题,枚举kkk以后,可以用步移方法解决:
S(n,m)=∑i=0mC(n,i)S(n,m+1)=S(n,m)+C(n,m+1)S(n,m−1)=S(n,m)−C(n,m)S(n+1,m)=∑i=0mC(n+1,i)=∑i=0mC(n,i)+C(n,i−1)=2S(n,m)−C(n,m)\begin{aligned} &S(n,m)=\sum_{i=0}^mC(n,i)\\ &S(n,m+1)=S(n,m)+C(n,m+1)\\ &S(n,m-1)=S(n,m)-C(n,m) \\ &S(n+1,m)=\sum_{i=0}^mC(n+1,i)=\sum_{i=0}^mC(n,i)+C(n,i-1)=2S(n,m)-C(n,m) \end{aligned} ​S(n,m)=i=0∑m​C(n,i)S(n,m+1)=S(n,m)+C(n,m+1)S(n,m−1)=S(n,m)−C(n,m)S(n+1,m)=i=0∑m​C(n+1,i)=i=0∑m​C(n,i)+C(n,i−1)=2S(n,m)−C(n,m)​

那么复杂度就是O(a+b)O(a+b)O(a+b)了。

#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
const int maxn=2e7+5,mod=1e9+9;
ll fac[maxn],ifac[maxn],pw[maxn];
ll ksm(ll x,ll p){ll ans=1;for(;p;p>>=1,x=x*x%mod) ans=(p&1)?ans*x%mod:ans;return ans;
}
ll C(ll n,ll m){if(n<m) return 0;return fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
signed main(){int n=yh()-2,m=yh()-2;fac[0]=1;pw[0]=1;rep(i,1,n+m) fac[i]=fac[i-1]*i%mod;ifac[n+m]=ksm(fac[n+m],mod-2);dwn(i,n+m-1,0) ifac[i]=ifac[i+1]*(i+1)%mod;ll ans1=0,ans2=0,S1=0,S2=0;rep(i,1,n+m) pw[i]=pw[i-1]*2%mod;rep(i,0,n+m){ll tmp=fac[i]*pw[i]%mod*C(n+m,i)%mod;ans1^=tmp;}cout<<ans1<<" ";rep(i,0,n) S1=(S1+C(n+m,i))%mod;ll i2=ksm(2ll,mod-2);S2=(S1-C(n+m,n)+mod)%mod;for(int i=0;i<=n+m;i++,S1=(S1+C(n+m-i,n))%mod*i2%mod,S2=(S2+C(n+m-i,n-i))%mod*i2%mod,S2=(S2-C(n+m-i,n-i)+mod)%mod){// cout<<S1<<" "<<S2<<hvie;ll tmp=fac[n]*fac[m]%mod*pw[i]%mod*ifac[n+m-i]%mod*(S1-S2+mod)%mod;ans2^=tmp;}cout<<ans2<<hvie;return 0;
}

C. Draw Grids

【题意】

给定一个n×mn\times mn×m的点阵,每次选择相邻两个点连线,两个人轮流操作,不能画出封闭图形,不能操作的人输。

【思路】

不能封闭即图始终是一片森林,那么终态一定是一颗生成树,因此根据点数的奇偶性即可判断结果。

【参考代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
const int maxn=3e5+5;int main(){int n,m;cin>>n>>m;if((n*m-1)&1){puts("YES");}else puts("NO");return 0;
}

D. Er Ba Game

【题目】

【思路】

按照题意模拟即可

【参考代码】

/** @date:2021-07-19 12:01:01* @source:
*/
#include <bits/stdc++.h>using namespace std;typedef pair<int, int> pii;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
#define fir first
#define sec second
#define ALL(x) (x).begin(), (x).end()
#define SZ(x) (int)x.size()
#define For(i, x) for (int i = 0; i < (x); ++i)
#define Trav(i, x) for (auto & i : x)
#define pb push_back
template<class T, class G> bool chkMax(T &x, G y) {return y > x ? x = y, 1 : 0;}
template<class T, class G> bool chkMin(T &x, G y) {return y < x ? x = y, 1 : 0;}int T;
int a1, a2, b1, b2;bool equal(int a, int b) {return a == b;}
bool big(int a, int b) {return a == 2 && b == 8; }int cmp(int a1, int b1, int a2, int b2) {if (a1 > b1) swap(a1, b1);if (a2 > b2) swap(a2, b2);if (a1 == a2 && b1 == b2) return 2;if (big(a1, b1) && !big(a2, b2)) return 0;if (big(a2, b2) && !big(a1, b1)) return 1;if (equal(a1, b1) && equal(a2, b2)) {return a1 < a2;}if (equal(a1, b1) && !equal(a2, b2)) return 0;if (equal(a2, b2) && !equal(a1, b1)) return 1;if ((a1 + b1) % 10 != (a2 + b2) % 10) {return (a1 + b1) % 10 < (a2 + b2) % 10;} else {return b1 < b2;}
}int main() {scanf("%d", &T);while (T--) {scanf("%d%d%d%d", &a1, &b1, &a2, &b2);int ans = cmp(a1, b1, a2, b2);if (ans == 0) puts("first");else if (ans == 1) puts("second");else puts("tie");}return 0;
}

E. Gas Station

【题意】

给定一棵带边权和点权的树,有QQQ次询问,每次从sss出发,初始权值是xxx,不允许经过某个点ppp。经过一条边消耗为边权,每到一个增加点权,要求过程中权值非负,求可以到达的点的个数。

n,Q≤105n,Q\leq 10^5n,Q≤105

【思路】

点分治,每次求跨过根的可达点的个数。

可以预处理每个点能不能到该根,从这个根下去需要多少初始权值。

讨论ppp所在位置,减去跨过ppp或自己子树内的部分,求子树内答案可以先对预处理的权值离散化,最后BIT+DFS维护。

复杂度O(nlog⁡2n)O(n\log^2 n)O(nlog2n),实现见代码。

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define int long long
using namespace std;typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
const int N=4e5+10,mod=1e9+7,inf=0x3f3f3f3f;int a[N],ans[N];
pii b[N];
vector<pii>G[N];
vector<int>vec[N];int read()
{int ret=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}while(isdigit(c)) ret=ret*10+(c^48),c=getchar();return f?ret:-ret;
}struct node
{int d,id,k;node(int d=0,int id=0,int k=0):d(d),id(id),k(k){}
};
vector<node>q[N];int siz[N],son[N],vis[N],rt,sum;void getroot(int x,int f)
{siz[x]=1;son[x]=0;for(auto t:G[x]){int v=t.fi;if(vis[v] || v==f) continue;getroot(v,x);siz[x]+=siz[v];son[x]=max(son[x],siz[v]);}son[x]=max(son[x],sum-siz[x]);if(son[x]<son[rt]) rt=x;
}struct BIT
{#define lowbit(x) (x&(-x))int c[N],lim;void init(int x){lim=x;memset(c,0,(x+2)*4);}void add(int x){for(;x<=lim;x+=lowbit(x)) c[x]++;}int query(int x){int ret=0;for(;x;x-=lowbit(x)) ret+=c[x];return ret;}
}bit;int cnt,dfn;
int H[N],st[N],ed[N],bl[N],A[N],B[N],D[N],fr[N];
void dfs1(int x,int f,int ds)
{H[++cnt]=-A[x];st[x]=++dfn;fr[dfn]=x;for(auto t:G[x]){int v=t.fi,w=t.se;if(vis[v] || v==f) continue;A[v]=min(A[x],ds-w);B[v]=min(B[x]+a[v]-w,0ll);D[v]=D[x]-w+a[v];bl[v]=f?bl[x]:v;dfs1(v,x,ds-w+a[v]);}ed[x]=dfn;
}
void dfs2(int x,int f)
{for(auto &i:q[x]) ans[i.id]-=i.k*bit.query(i.d=upper_bound(H+1,H+cnt+1,i.d)-H-1);bit.add(lower_bound(H+1,H+cnt+1,-A[x])-H);for(auto v:G[x])if(!vis[v.fi] && v.fi!=f) dfs2(v.fi,x);for(auto &i:q[x]) ans[i.id]+=i.k*bit.query(i.d);ed[x]=0;q[x].clear();
}void solve(int x)
{vis[x]=1;dfn=A[x]=B[x]=D[x]=bl[x]=cnt=0;dfs1(x,0,a[x]);sort(H+1,H+cnt+1);cnt=unique(H+1,H+cnt+1)-H-1;for(int j=1;j<=dfn;++j){int u=fr[j];for(auto i:vec[u]){if(B[u]+b[i].fi<0) continue;//can't to rtif(st[b[i].se]<=st[u] && ed[u]<=ed[b[i].se]) continue;//blockedint ds=D[u]+b[i].fi;q[x].pb(node(ds,i,1));if(bl[u]) q[bl[u]].pb(node(ds,i,-1));if(ed[b[i].se] && (st[b[i].se]<st[bl[u]] || st[b[i].se]>ed[bl[u]]))q[b[i].se].pb(node(ds,i,-1));}}bit.init(cnt);dfs2(x,0);for(auto v:G[x]){if(vis[v.fi]) continue;rt=0;sum=siz[v.fi];getroot(v.fi,x);solve(rt);}
}signed main()
{int n=read(),Q=read();for(int i=2;i<=n;++i){int u=read(),v=read(),w=read();G[u].pb(mkp(v,w));G[v].pb(mkp(u,w));}for(int i=1;i<=n;++i) a[i]=read();for(int i=1;i<=Q;++i){int x=read(),td=read(),tp=read();vec[x].pb(i);b[i]=mkp(td,tp);}son[0]=sum=n;getroot(1,0);solve(rt);for(int i=1;i<=Q;++i) printf("%lld\n",ans[i]);return 0;
}

F. Girlfriend

【题意】

空间中有六个点,满足∣P1A∣≥k1∣P1B∣,∣P2C∣≥k2∣P2D∣|P_1A|\geq k_1|P_1B|,|P_2C|\geq k_2|P_2D|∣P1​A∣≥k1​∣P1​B∣,∣P2​C∣≥k2​∣P2​D∣

求P1,P2P_1,P_2P1​,P2​各自轨迹围成的空间体的体积交

【思路】

对于固定的kkk,事实上PPP的轨迹围成一个阿波罗尼斯球,这个通过解上面不等式的方程可以的出来结论。

那么问题就可以转化为求球的体积交。

【参考代码】

#include<bits/stdc++.h>
#define double long double
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
struct vec{double x,y,z;vec(){}vec(double x,double y,double z):x(x),y(y),z(z){}
};
void trans(double A,double B,double C,double D,vec&P,double &R2){P=vec(-A/2.,-B/2.,-C/2.);R2=(A*A+B*B+C*C)/4-D;// cout<<P.x<<" "<<P.y<<" "<<P.z<<" "<<R2<<hvie;
}
double k1,k2;
void toabcd(double k,double x1,double y1,double z1,double x2,double y2,double z2,double &A,double &B,double &C,double &D){A=-2*k*x2+2*x1;B=-2*k*y2+2*y1;C=-2*k*z2+2*z1;D=k*(x2*x2+y2*y2+z2*z2)-(x1*x1+y1*y1+z1*z1);A/=k-1; B/=k-1; C/=k-1; D/=k-1;
}
//2球体积交
double pi;
double pow2(double x){return x*x;
}
double pow3(double x){return x*x*x;}
double dis(double x,double y,double z,double a,double b,double c){return (pow2(x-a)+pow2(y-b)+pow2(z-c));
}
double cos(double a,double b,double c){return (b*b+c*c-a*a)/(2*b*c);}
double cap(double r,double h){return pi*(r*3-h)*h*h/3;}
double sphere_intersect(double x1,double y1,double z1,double r1,double x2,double y2,double z2,double r2)
{double d=dis(x1,y1,z1,x2,y2,z2);//相离if(d>=pow2(r1+r2))return 0;//包含if(d<=pow2(r1-r2))return pow3(min(r1,r2))*4*pi/3;//相交double h1=r1-r1*cos(r2,r1,sqrt(d)),h2=r2-r2*cos(r1,r2,sqrt(d));return cap(r1,h1)+cap(r2,h2);
}int main(){pi=acos(-1);dwn(_,yh(),1){vec A,B,C,D;A.x=yh();A.y=yh();A.z=yh();B.x=yh();B.y=yh();B.z=yh();C.x=yh();C.y=yh();C.z=yh();D.x=yh();D.y=yh();D.z=yh();k1=yh();k2=yh();k1=k1*k1;k2=k2*k2;double a,b,c,d;vec C1,C2;double r1,r2;toabcd(k1,A.x,A.y,A.z,B.x,B.y,B.z,a,b,c,d);trans(a,b,c,d,C1,r1);r1=sqrt(r1);toabcd(k2,C.x,C.y,C.z,D.x,D.y,D.z,a,b,c,d);trans(a,b,c,d,C2,r2);r2=sqrt(r2);cout<<fixed<<setprecision(10)<<sphere_intersect(C1.x,C1.y,C1.z,r1,C2.x,C2.y,C2.z,r2)<<hvie;}return 0;
}

G. League of Legends

【题意】

给定nnn个区间,要求将它们分成kkk组,每组要有交,最大化每组交长度之和。

n,k≤5000n,k\leq 5000n,k≤5000

【思路】

首先一个显然的简化是,对于每一个包含其他区间的大区间,它的最优决策有两种:

  • 归属到一个被它包含区间所在的组,这样不影响答案
  • 独自为一组,长度直接计入答案

那么剩下的部分就是左右端点均单调递增的小区间了,排序后,记f[i][j]f[i][j]f[i][j]表示前iii个,分了jjj组的最优值,那么转移就是f[i][j]+r[i]−l[j]→f[k][j+1]f[i][j]+r[i]-l[j]\rightarrow f[k][j+1]f[i][j]+r[i]−l[j]→f[k][j+1],且要满足r[i]>l[j]r[i]>l[j]r[i]>l[j],这个显然可以用单调队列优化。

最后求出了f[n′][i]f[n'][i]f[n′][i]以后,综合大区间的贡献,枚举这个iii讨论区多少个大区间即可。

复杂度O(n2)O(n^2)O(n2)

【参考代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
const int maxn=3e5+5,inf=0x3f3f3f3f;
pii s[maxn];
int n,m;
int q[maxn];
ll b[maxn];
ll f[5005][5005];
int main(){n=yh(),m=yh();rep(i,1,n) s[i].fi=yh(),s[i].se=yh();sort(s+1,s+1+n);int mn=inf,cnt=0;dwn(i,n,1){if(s[i].se>=mn) b[++cnt]=s[i].se-s[i].fi, s[i].fi=inf;else mn=s[i].se;}memset(f,~0x3f,sizeof(f));f[0][0]=0;sort(s+1,s+1+n);n-=cnt;// rep(i,1,n)cout<<s[i].fi<<" "<<s[i].se<<hvie;rep(j,1,m){int l=0,r=0;q[0]=0;rep(i,1,n){while(l<=r&&s[q[l]+1].se<=s[i].fi) l++;if(l<=r) f[i][j]= f[q[l]][j-1] +s [q[l]+1].se-s[i].fi;// cout<<i<<","<<j<<"  "<<q[l]<<" "<<f[i][j]<<hvie;while(l<=r&&f[i][j-1]+s[i+1].se >= f[q[r]][j-1]+s[q[r]+1].se) r--;q[++r]=i;}}sort(b+1,b+1+cnt,greater<ll>());ll ans=0;rep(i,2,cnt) b[i]+=b[i-1];rep(i,1,m){if(m-i>cnt)continue;ans=max(ans,f[n][i]+b[m-i]);}cout<<ans<<hvie;return 0;
}

H. Olefin

【题意】

给定一个长度为nnn的01串,每次翻转一个两端为1的交替段,求连续翻转kkk次的方案数。

1≤n,k≤6×1051\leq n,k\leq 6\times 10^51≤n,k≤6×105

【思路】

还是不太会。

由连续多个0分隔开的01段实际上是相互独立的,那么我们只需要考虑下面的子问题:

  • 包含nnn个1的交替01段连续操作kkk次的方案数

然后我们把每个段的答案用NTT合并EGF即可。

首先我们可以由一个Naive的思路:每次操作后会将01段分成三个独立的段,用分布累和来优化暴力DP可以做到O(n3)O(n^3)O(n3)

然后据说通过打表可以发现nnn个1的交替序列答案是:
(n+kn−k)∏i=1k(2i−1)\binom{n+k}{n-k}\prod_{i=1}^k(2i-1) (n−kn+k​)i=1∏k​(2i−1)
然后就可以线性预处理O(nlog⁡2n)O(n\log^2n)O(nlog2n)合并了。

事实上由于合并长度不到串长的一半,而且分治NTT很快,就可以过了。

上面这个式子是有具体意义的:

  • (n+kn−k)\binom{n+k}{n-k}(n−kn+k​)表示在最终串中放n−kn-kn−k个不相邻的1的方案数
  • ∏i=1k(2i−1)\prod_{i=1}^k(2i-1)∏i=1k​(2i−1)表示每一个操作kkk次的终态对应的不同操作序列数,这个可以通过归纳法证明,具体如下:

k=1k=1k=1 显然方案数为1

对于一个k+1k+1k+1次操作的终止态,考虑复原其所有可行的$k $次父态 ,只需证明这样的父态恰有2k+12k+12k+1个

容易发现一个到父态的一个逆操作一定是:翻转一段两端为000的 极长 的交替序列;如果不是极长,在复原的序列中会出现相邻的1

假设在序列中,我们在末尾添加一个额外的0,然后让每一个形如10 的对互相抵消,则剩下的序列只包含0,且每一个0(除了最后一个)恰好可以代表一段极长的逆操作序列

例如: 010000 转化为 0000 ,其合法的逆操作为 (1,3) (4,4) (5,5); 010010转化为 00 (最后一个0抵消掉了),其合法的逆操作为 (1,3) (4,4)

由此,k+1k+1k+1轮操作的合法逆操作数为 2n−1−2(n−k−1)=2k+12n-1-2(n-k-1)=2k+12n−1−2(n−k−1)=2k+1 *,*即其父态个数

因此结论归纳成立 ,证毕。

【参考代码】 (std)

#include <bits/stdc++.h>
constexpr int P = 998244353;
std::vector<int> rev, roots{0, 1};
int power(int a, int b) {int res = 1;for (; b; b >>= 1, a = 1ll * a * a % P)if (b & 1)res = 1ll * res * a % P;return res;
}
void dft(std::vector<int> &a) {int n = a.size();if (int(rev.size()) != n) {int k = __builtin_ctz(n) - 1;rev.resize(n);for (int i = 0; i < n; ++i)rev[i] = rev[i >> 1] >> 1 | (i & 1) << k;}for (int i = 0; i < n; ++i)if (rev[i] < i)std::swap(a[i], a[rev[i]]);if (int(roots.size()) < n) {int k = __builtin_ctz(roots.size());roots.resize(n);while ((1 << k) < n) {int e = power(3, (P - 1) >> (k + 1));for (int i = 1 << (k - 1); i < (1 << k); ++i) {roots[2 * i] = roots[i];roots[2 * i + 1] = 1ll * roots[i] * e % P;}++k;}}for (int k = 1; k < n; k *= 2) {for (int i = 0; i < n; i += 2 * k) {for (int j = 0; j < k; ++j) {int u = a[i + j];int v = 1ll * a[i + j + k] * roots[k + j] % P;int x = u + v;if (x >= P)x -= P;a[i + j] = x;x = u - v;if (x < 0)x += P;a[i + j + k] = x;}}}
}
void idft(std::vector<int> &a) {int n = a.size();std::reverse(a.begin() + 1, a.end());dft(a);int inv = power(n, P - 2);for (int i = 0; i < n; ++i)a[i] = 1ll * a[i] * inv % P;
}
struct Poly {std::vector<int> a;Poly() {}Poly(int a0) {if (a0)a = {a0};}Poly(const std::vector<int> &a1) : a(a1) {while (!a.empty() && !a.back())a.pop_back();}int size() const {return a.size();}int operator[](int idx) const {if (idx < 0 || idx >= size())return 0;return a[idx];}Poly mulxk(int k) const {auto b = a;b.insert(b.begin(), k, 0);return Poly(b);}Poly modxk(int k) const {k = std::min(k, size());return Poly(std::vector<int>(a.begin(), a.begin() + k));}Poly divxk(int k) const {if (size() <= k)return Poly();return Poly(std::vector<int>(a.begin() + k, a.end()));}friend Poly operator+(const Poly a, const Poly &b) {std::vector<int> res(std::max(a.size(), b.size()));for (int i = 0; i < int(res.size()); ++i) {res[i] = a[i] + b[i];if (res[i] >= P)res[i] -= P;}return Poly(res);}friend Poly operator-(const Poly a, const Poly &b) {std::vector<int> res(std::max(a.size(), b.size()));for (int i = 0; i < int(res.size()); ++i) {res[i] = a[i] - b[i];if (res[i] < 0)res[i] += P;}return Poly(res);}friend Poly operator*(Poly a, Poly b) {int sz = 1, tot = a.size() + b.size() - 1;while (sz < tot)sz *= 2;a.a.resize(sz);b.a.resize(sz);dft(a.a);dft(b.a);for (int i = 0; i < sz; ++i)a.a[i] = 1ll * a[i] * b[i] % P;idft(a.a);return Poly(a.a);}Poly &operator+=(Poly b) {return (*this) = (*this) + b;}Poly &operator-=(Poly b) {return (*this) = (*this) - b;}Poly &operator*=(Poly b) {return (*this) = (*this) * b;}Poly deriv() const {if (a.empty())return Poly();std::vector<int> res(size() - 1);for (int i = 0; i < size() - 1; ++i)res[i] = 1ll * (i + 1) * a[i + 1] % P;return Poly(res);}Poly integr() const {if (a.empty())return Poly();std::vector<int> res(size() + 1);for (int i = 0; i < size(); ++i)res[i + 1] = 1ll * a[i] * power(i + 1, P - 2) % P;return Poly(res);}Poly inv(int m) const {Poly x(power(a[0], P - 2));int k = 1;while (k < m) {k *= 2;x = (x * (2 - modxk(k) * x)).modxk(k);}return x.modxk(m);}Poly log(int m) const {return (deriv() * inv(m)).integr().modxk(m);}Poly exp(int m) const {Poly x(1);int k = 1;while (k < m) {k *= 2;x = (x * (1 - x.log(k) + modxk(k))).modxk(k);}return x.modxk(m);}Poly sqrt(int m) const {Poly x(1);int k = 1;while (k < m) {k *= 2;x = (x + (modxk(k) * x.inv(k)).modxk(k)) * ((P + 1) / 2);}return x.modxk(m);}Poly mulT(Poly b) const {if (b.size() == 0)return Poly();int n = b.size();std::reverse(b.a.begin(), b.a.end());return ((*this) * b).divxk(n - 1);}std::vector<int> eval(std::vector<int> x) const {if (size() == 0)return std::vector<int>(x.size(), 0);const int n = std::max(int(x.size()), size());std::vector<Poly> q(4 * n);std::vector<int> ans(x.size());x.resize(n);std::function<void(int, int, int)> build = [&](int p, int l, int r) {if (r - l == 1) {q[p] = std::vector<int>{1, (P - x[l]) % P};} else {int m = (l + r) / 2;build(2 * p, l, m);build(2 * p + 1, m, r);q[p] = q[2 * p] * q[2 * p + 1];}};build(1, 0, n);std::function<void(int, int, int, const Poly &)> work = [&](int p, int l, int r, const Poly &num) {if (r - l == 1) {if (l < int(ans.size()))ans[l] = num[0];} else {int m = (l + r) / 2;work(2 * p, l, m, num.mulT(q[2 * p + 1]).modxk(m - l));work(2 * p + 1, m, r, num.mulT(q[2 * p]).modxk(r - m));}};work(1, 0, n, mulT(q[1].inv(n)));return ans;}
};constexpr int N = 600000;int fac[N + 1], invfac[N + 1], inv2[N + 1];int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n, k;std::cin >> n >> k;fac[0] = 1;inv2[0] = 1;for (int i = 1; i <= N; i++) {fac[i] = 1LL * fac[i - 1] * i % P;inv2[i] = 1LL * inv2[i - 1] * (P + 1) / 2 % P;}invfac[N] = power(fac[N], P - 2);for (int i = N; i > 0; i--) {invfac[i - 1] = 1LL * invfac[i] * i % P;}std::string s;std::cin >> s;std::vector<int> pos;pos.reserve((n + 1) / 2);for (int i = 0; i < n; i++) {if (s[i] == '1') {pos.push_back(i);}}std::vector<int> len;n = pos.size();for (int l = 0, r; l < n; l = r) {for (r = l + 1; r < n && pos[r - 1] + 2 == pos[r]; r++);len.push_back(r - l);}if (k > n) {std::cout << "0\n";return 0;}auto solve = [&](auto self, int l, int r) -> Poly {if (r - l == 1) {int m = len[l];std::vector<int> a(m + 1);for (int i = 0; i <= m; i++) {a[i] = 1LL * fac[m + i] * invfac[m - i] % P * invfac[i] % P * invfac[i] % P * inv2[i] % P;}return Poly(a);}int m = (l + r) / 2;return self(self, l, m) * self(self, m, r);};std::cout << 1LL * solve(solve, 0, len.size())[k] * fac[k] % P << "\n";return 0;
}

I. Penguins

【题意】

两个20×2020\times 2020×20的01矩阵,要求第一个矩阵中的企鹅从(20,20)(20,20)(20,20)移动到(1,20)(1,20)(1,20),第二个矩阵中的企鹅从(20,1)(20,1)(20,1)移动到(1,1)(1,1)(1,1),两个企鹅的移动方式是关于y轴镜像的。你可以操纵第一个矩阵中的企鹅,求最短的字典序最小按键顺序,

【思路】

记一个四维的状态表示最短路,并顺便记录下怎么来的,然后bfs模拟即可。

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
const int N=2e5+10,M=22,mod=1e9+7,inf=0x3f3f3f3f;int read()
{int ret=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}while(isdigit(c)) ret=ret*10+(c^48),c=getchar();return f?ret:-ret;
}int las[N],dis[N];
char mp[2][M][M];
int id[M][M][M][M];
stack<char>st;int dya[]={0,-1,1,0},dxa[]={1,0,0,-1};
int dyb[]={0,1,-1,0},dxb[]={1,0,0,-1};struct Tpos
{int xa,ya,xb,yb;Tpos(int xa=0,int ya=0,int xb=0,int yb=0):xa(xa),ya(ya),xb(xb),yb(yb){}
};
queue<Tpos>q;int getid(int a,int b,int c,int d)
{return id[a][b][c][d];
}
int getidp(const Tpos&x)
{return getid(x.xa,x.ya,x.xb,x.yb);
}
bool inmp(int x,int y,int op)
{return 1<=x && x<=20 && 1<=y && y<=20 && mp[op][x][y]=='.';
}void print()
{//puts("print!");int goal=getidp(Tpos(1,20,1,1)),ans=0;do{int xa=goal/21/21/21,ya=goal/21/21%21,xb=goal/21%21,yb=goal%21;mp[0][xa][ya]=mp[1][xb][yb]='A';if(las[goal]!=-1){++ans;int nxa=las[goal]/21/21/21,nya=las[goal]/21/21%21;if(nxa==xa && nya==ya){int nxb=las[goal]/21%21,nyb=las[goal]%21;if(nxb==xb){if(nyb==yb+1) st.push('R');else st.push('L');}else{if(nxb==xb+1) st.push('U');else st.push('D');} }else{if(nxa==xa){if(nya==ya+1) st.push('L');else st.push('R');}else{if(nxa==xa+1) st.push('U');else st.push('D');} }}goal=las[goal];}while(las[goal]!=-1);printf("%d\n",ans);while(!st.empty()) putchar(st.top()),st.pop();puts("");mp[0][20][20]=mp[1][20][1]='A';for(int i=1;i<=20;++i){printf("%s %s\n",mp[0][i]+1,mp[1][i]+1);}}void bfs()
{Tpos p=Tpos(20,20,20,1),goal=Tpos(1,20,1,1);int tp=getidp(p),tgoal=getidp(goal);memset(dis,-1,sizeof(dis));memset(las,-1,sizeof(las));dis[tp]=0;q.push(p);while(!q.empty()){p=q.front();tp=getidp(p);q.pop();// printf("%d %d %d %d\n",p.xa,p.ya,p.xb,p.yb);if(tp==tgoal){print();return;}for(int i=0;i<4;++i){int nxa=p.xa+dxa[i],nya=p.ya+dya[i],nxb=p.xb+dxb[i],nyb=p.yb+dyb[i];if(!inmp(nxa,nya,0)) nxa=p.xa,nya=p.ya;if(!inmp(nxb,nyb,1)) nxb=p.xb,nyb=p.yb;Tpos np=Tpos(nxa,nya,nxb,nyb);//printf("test:%d %d %d %d\n",nxa,nya,nxb,nyb);int tnp=getidp(np);if(dis[tnp]==-1) {// printf("%d\n",++cnt);dis[tnp]=dis[tp]+1;las[tnp]=tp;q.push(np);}if(tnp==tgoal){print();return;}}}//puts("!!");
}signed main()
{for(int i=1;i<=20;++i) {scanf("%s",mp[0][i]+1);scanf("%s",mp[1][i]+1);}for(int a=1;a<=20;++a) for(int b=1;b<=20;++b)for(int c=1;c<=20;++c) for(int d=1;d<=20;++d)id[a][b][c][d]=a*21*21*21+b*21*21+c*21+d;bfs();return 0;
}

J. Product of GCDs

【题意】

给定一个集合,求它所有大小为kkk的子集的gcd的积,对PPP取模,有TTT组数据

T≤60,k≤30,P≤1014T\leq 60,k\leq 30,P\leq 10^{14}T≤60,k≤30,P≤1014,数字不超过8×1048\times 10^48×104

【思路】

考虑对于每一个质因子分别统计答案,计算f[p][c]f[p][c]f[p][c]表示至少包含pcp^cpc的数的个数,那么这里方案数是(f[p][c]k)\binom{f[p][c]} {k}(kf[p][c]​),直接对于差分累加即可。注意这里计算得到的答案是一个指数,最后要做快速幂,所以我们这里要对φ(p)\varphi(p)φ(p)取模,求这个东西可以Miller-Robin啥的求一下。

这里求出了至少的,再做一个O(xlog⁡x)O(x\log x)O(xlogx)的容斥就行。

容斥还需要O(xlog⁡x)O(\frac x {\log x})O(logxx​)次快速幂,所以统计答案是O(x)O(x)O(x)的。

比赛队友写的不是这个写法,但大体上还是考虑每个质因子的贡献,卡常倒是卡了半天。

【参考代码】

#include<bits/stdc++.h>
#define int long long
// #define int __int128
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh() {int ret = 0; bool f = 0; char c = getchar();while (!isdigit(c)) {if (c == EOF)return -1;if (c == '-')f = 1;c = getchar();}while (isdigit(c))ret = (ret << 3) + (ret << 1) + (c ^ 48), c = getchar();return f ? -ret : ret;
}
const int maxn = 3e5 + 5;
int n, k, mod;
ll prime[99999], cnt;
ll gcd(ll a, ll b) {if (!b) return a;return gcd(b, a % b);
}
ll mmul(ll x, ll y, ll mod) { //快速加ll ans = 0;while (y) {if (y % (1ll * 2)) ans = (ans + x % mod) % mod;y /= (1ll * 2);x = (x % mod + x % mod) % mod;}return ans;
}
ll fast_pow(ll x, ll y, ll m) { //快速幂ll ans = 1;x %= m;while (y) {if (y % 2)ans = mmul(ans, x, m);y /= 2;x = mmul(x, x, m);}return ans;
}
bool MR(ll n) {if (n == 2) return 1;if (n < 2 || !(n & 1)) return 0;ll m = n - 1;ll k = 0;while ((k & 1) == 0) {k++;m /= 2;}for (int i = 0; i < 12; i++) {ll a = rand() % (n - 1) + 1;ll x = fast_pow(a, m, n);ll y = 0;for (int j = 0; j < k; j++) {y = mmul(x, x, n);if (y == 1 && x != 1 && x != n - 1) return 0;x = y;}if (y != 1) return 0;}return 1;
}
ll rho(ll n, ll c) { //找因子ll i = 1, k = 2;ll x = rand() % (n - 1) + 1;ll y = x;while (1) {i++;x = (mmul(x, x, n) + c) % n;ll d = gcd(((y - x) + n) % n, n) % n;if (d > 1 && d < n) return d;if (y == x) return n;if (i == k) { //一个优化,我TM也不知道为啥y = x;k <<= 1;}}
}
void find(ll n, ll c) { //分解的递归过程if (n == 1) return;if (MR(n)) {prime[++cnt] = n;return;}ll p = n;ll k = c;while (p >= n) p = rho(n, c--);find(n / p, c);find(p, c);
}
ll getphi(ll n) {cnt = 0;find(n, 120);sort(prime + 1, prime + cnt + 1);ll t = unique(prime + 1, prime + cnt + 1) - prime - 1;double ans = n;for (int i = 1; i <= t; i++) ans *= (1.0 - (1.0 / (double)prime[i]));return ans;
}struct num {int x; bool bg;
};
int P;
num operator+(const num &a, const num &b) {bool bg = 0;if (a.bg || b.bg || a.x + b.x >= P) bg = 1;return {(a.x + b.x) % P, bg};
}
// num mul(num x,num b){//  num ans={0,0};
//  while(b.x>0){//      if(b.x&1) ans=(ans+x);
//      x=(x+x);
//      b={b.x/2,b.bg};
//  }
//  return ans;
// }
num mul(num a, num b) {__int128 A = a.x, B = b.x, C = A * B;return num{(ll)(C % P), a.bg || b.bg || C >= P};
}
inline ll mul(ll a, ll b, ll p) {if (p <= 1000000000) return a * b % p;else if (p <= 1000000000000ll) return (((a * (b >> 20) % p) << 20) + (a * (b & ((1 << 20) - 1)))) % p;else {ll d = (ll)floor(a * (long double)b / p + 0.5);ll ret = (a * b - d * p) % p;if (ret < 0) ret += p;return ret;}
}
int mmmul(int x, int b, int mod) {int ans = 0;while (b) {if (b & 1) ans = (ans + x) % mod;x = (x + x) % mod;b >>= 1;}return ans;
}
void _fac(int x, map<int, vector<int>> &tms) {for (int i = 2; i * i <= x; i++)if (x % i == 0) {int tmp = 0;while (x % i == 0) {tmp++;x /= i;}tms[i].pb(tmp);}if (x > 1)tms[x].pb(1);
}
int ksm(int x, int p, int mod) {int ans = 1;while (p) {if (p & 1) ans = mul(ans, x, mod) % mod;x = mul(x, x, mod) % mod;p >>= 1;}return ans;
}
int phi(int x) {int ans = x;for (int i = 2; i * i <= x; i++)if (x % i == 0) {ans /= i; ans *= (i - 1);while (x % i == 0)x /= i;}if (x > 1) ans = ans / x * (x - 1);return ans;
}
num C[maxn][31];
int prm[maxn], TOT = 0, minp[maxn]; bool vis[maxn];
void euler(int n) {for (int i = 2; i <= n; i++) {if (!vis[i]) prm[++TOT] = i, minp[i] = i;for (int j = 1; j <= TOT && prm[j]*i <= n; j++) {vis[i * prm[j]] = 1;minp[i * prm[j]] = min(minp[i], prm[j]);//if (i % prm[j] == 0)break;}}
}
vector<pii> _FAC(int x) {map<int, int>t;while (x > 1) {t[minp[x]]++;x /= minp[x];}vector<pii>ret;for (auto &p : t) {ret.pb({p.fi, p.se});}return ret;
}
vector<pii>yin[maxn];
map<int, vector<int>>tms;
signed main() {// freopen("my.in","r",stdin);euler(1e5);rep(i, 1, 8e4) {yin[i] = _FAC(i);}dwn(_, yh(), 1) {tms.clear();n = yh(); k = yh(); mod = yh();P = getphi(mod);C[0][0] = {1, 1 >= P};// cerr<<"A";rep(i, 1, n) {C[i][0] = {1, 1 >= P};rep(j, 1, min(k, i)) {C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]);}}// cerr<<"B";rep(i, 1, n) {int x = yh();for (auto &p : yin[x]) {tms[p.fi].pb(p.se);}// _FAC(x,tms);// _fac(x,tms);}// cerr<<"C";int ans = 1;// cerr<<clock()<<endl;for (auto &i : tms) {int l, r, up;vector<int> &v = i.se;sort(v.begin(), v.end());num tot = {0, 0};for (l = 0, up = v.size(); l < up; l = r + 1) {r = l;while (r + 1 < up && v[r + 1] == v[l]) r++;int t = v[l], len = r - l + 1;int gt = up - r - 1;num tmp = {0, 0};rep(o, 1, min(k, len)) {int other = k - o;tmp = tmp + mul(C[len][o], C[gt][other]);}tmp = mul(tmp, num{t, t >= P});tot = (tot + tmp);}if (tot.bg) ans = mul(ans, ksm(i.fi, tot.x + P, mod), mod) % mod;else ans = mul(ans, ksm(i.fi, tot.x, mod), mod) % mod;}// cerr<<clock()<<endl;// cerr<<"D";ll A = ans % mod;printf("%lld\n", A);//cerr<<"!\n";}return 0;
}
/*
1
10 4 3266841504299
23340 78378 42822 50742 19386 53374 24139 25166 65404 33240
*/

K. Stack

【题意】

已知一个序列若干时刻单调栈(栈顶最大)的大小,构造一个合法的序列。

【思路】

对于一个没有给定单调栈大小的位置,我们一定是一直往栈顶放元素不弹出,这样一定能构造出一组合法解;如果弹出,考虑[1,?,3]这样的序列就知道不一定合法了。

接着我们可以根据单调栈元素的变化构造出一系列拓扑关系,直接拓扑排序就可以得到一组合法解了。

【参考代码】

/** @date:2021-07-19 13:25:08* @source:
*/
#include <bits/stdc++.h>using namespace std;typedef pair<int, int> pii;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
#define fir first
#define sec second
#define ALL(x) (x).begin(), (x).end()
#define SZ(x) (int)x.size()
#define For(i, x) for (int i = 0; i < (x); ++i)
#define Trav(i, x) for (auto & i : x)
#define pb push_back
template<class T, class G> bool chkMax(T &x, G y) {return y > x ? x = y, 1 : 0;
}
template<class T, class G> bool chkMin(T &x, G y) {return y < x ? x = y, 1 : 0;
}const int MAXN = 1e6 + 5;int N, K;
int B[MAXN];
vector<pii> v;
stack<int>st;
queue<int>q;
vector<int>G[MAXN];
int in[MAXN], num[MAXN];void add(int x, int y) {G[x].push_back(y); ++in[y];
//printf("%d %d\n",x,y);
}
void getans(int *a, int n) {for (int i = 1; i <= n; ++i) {if (a[i] > a[i - 1]) {if (!st.empty()) add(i, st.top());st.push(i);} else {int t = a[i - 1] - a[i] + 1;while (t) {add(st.top(), i);st.pop(); --t;}if (!st.empty()) add(i, st.top());st.push(i);}}while (!st.empty()) {int t = st.top();st.pop();if (st.empty()) break;add(t, st.top());}for (int i = 1; i <= n; ++i) if (!in[i]) q.push(i);int cnt = n;while (!q.empty()) {int x = q.front(); q.pop();num[x] = cnt--;for (auto v : G[x]) {--in[v];if (!in[v]) q.push(v);}}for (int i = 1; i <= n; ++i) printf("%d ", num[i]);
}int main() {scanf("%d%d", &N, &K);for (int i = 1, x, y; i <= K; ++i) {scanf("%d%d", &x, &y);B[x] = y;v.push_back({x, y});if (y > x) {puts("-1");return 0;}}sort(ALL(v));for (int i = 1; i < K; ++i) {if (v[i].fir - v[i].sec < v[i - 1].fir - v[i - 1].sec) {puts("-1");return 0;}}B[1] = 1;for (int i = 1; i <= N; ++i) {if (!B[i]) B[i] = B[i - 1] + 1;}getans(B, N);return 0;
}

L. Wechat Walk

【题意】

给定nnn个人之间的好友关系,每次单点增加一个人的步数,求每个人在自己列表里保持冠军的时间总长。

n≤2×105n\leq 2\times 10^5n≤2×105,保证一个人一天总共不会走超过10410^4104步

【思路】

直接做不太好做,但是这里有个很特殊的权值值域W=10000W=10000W=10000,我们考虑分块。

设S=nS=\sqrt nS=n​,我们称度数≤S\leq S≤S的点为小点,度数>S>S>S的点为大点

依次考虑每一个时刻,维护每个人成为冠军的区间。

假设被修改的点为xxx,容易发现有以下情况:

  • 如果xxx原来是冠军,无需修改冠军情况
  • 如果xxx不是冠军,设原来权值为aaa,新权值为bbb,a<ba<ba<b那么:
    • xxx是小点:直接枚举更新所有和xxx相邻的点,更新他们的冠军情况,那么也能同时知道xxx是否成为了冠军
    • xxx是大点:对于xxx周围的大点,同样直接枚举进行更新;对于xxx周围的小点,注意到WWW比较小,因此可以直接暴力存下和xxx相邻的小点中,权值为www的且为冠军的点集Cx,wC_{x,w}Cx,w​,成为冠军的时间只有在每次小点被增加的时候出现,那么CCC的总元素个数就是O(nS)O(nS)O(nS)的了。我们可以暴力扫描权值,判断i∈(a,b],Cx,ii\in(a,b],C_{x,i}i∈(a,b],Cx,i​中的点是否不再成为冠军,我们不需要维护CCC的删除操作。不难发现,元素和集合不会重复被扫描。

总的复杂度是O(SW+nS)O(SW+nS)O(SW+nS)的。

事实上WWW无限制的时候也是可以做的,因为离散化以后仍然可以像上面这么做。

【参考代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
const int maxn=2e5+5;int main(){int n=yh(),m=yh(),q=yh();vector<vector<int>>adj(n+1),nb(n+1);vector<vector<pii>>tick(10001);vector<int>now(n+1,q),last(n+1,q),mn(n+1,q),ans(n+1,0),walks(n+1,0);int SZ=sqrt(n);rep(i,1,m){int x=yh(),y=yh();adj[x].pb(y);adj[y].pb(x);}rep(i,1,n){if((int)adj[i].size()>=SZ){for(auto &j:adj[i]){nb[j].pb(i);}}}rep(i,1,q){int x=yh(),val=yh();walks[x]+=val;tick[walks[x]].pb({x,i});}dwn(w,10000,1){for(auto [x,t]:tick[w]){for(auto &y:nb[x]){mn[y]=min(mn[y],t);}last[x]=now[x];now[x]=t;}for(auto [x,t]:tick[w]){if((int)adj[x].size()<SZ){int r=last[x];for(auto y:adj[x]){r=min(r,now[y]);}ans[x]+=max(0,r-t);}else{ans[x]+=max(0,min(mn[x],last[x])-t);}}}rep(i,1,n) cout<<ans[i]<<"\n";return 0;
}

【多校训练】2021牛客多校第二场相关推荐

  1. LCS(2021牛客多校4)

    LCS(2021牛客多校4) 题意: 让你构造三个字符串s1,s2,s3,长度均为n,要求LCS(s1,s2)=a,LCS(s2,s3)=b,LCS(s1,s3)=c 题解: 先考虑三个串互相LCS为 ...

  2. 【2021牛客多校2】F-Girlfriend 计算几何

    2021牛客多校2-F F-Girlfriend 题目大意 给出四个点 A , B , C , D A, B, C, D A,B,C,D 另有两点 P 1 , P 2 P_1, P_2 P1​,P2​ ...

  3. 【2021牛客寒假第五场】B-比武招亲(上)排列组合

    [2021牛客寒假第五场]B-比武招亲(上)排列组合 题意 思路 Code(44MS) 传送门: https://ac.nowcoder.com/acm/contest/9985/B 题意 思路 考 ...

  4. 【2021牛客寒假第五场】C-比武招亲(下)欧拉降幂+多项式求逆预处理伯努利数计算等幂求和

    [2021牛客寒假第五场]C-比武招亲(下)欧拉降幂+多项式求逆预处理伯努利数计算等幂求和 前置技能 题意 思路 Code(715MS) 传送门: https://ac.nowcoder.com/ac ...

  5. K-Stack 2021牛客多校2

    链接:https://ac.nowcoder.com/acm/contest/11253/K 来源:牛客网 题目描述 ZYT had a magic permutation a1,a2,⋯ ,an a ...

  6. 2021牛客多校第八场补题 D-OR

    链接:https://ac.nowcoder.com/acm/contest/11259/D 来源:牛客网 题目描述 There are two sequences of length n−1n-1n ...

  7. 2021牛客多校第五场补题

    B-Boxes 链接:https://ac.nowcoder.com/acm/contest/11256/B 来源:牛客网 题目描述 There're nn_{}n​ boxes in front o ...

  8. 【多校训练】2021牛客多校5

    [前言] 很久没有时间整理题解了,补题和打游戏的时间居多(doge) 这场其实主要F出题人数据有锅,花太多时间了(赛后重测是一血),然后后面G想歪了爆搜剪枝没过,I的回滚莫队队友前一天写了结果今天写不 ...

  9. 【多校训练】2021牛客多校第一场

    [前言] 组队训练的第一场比赛,感觉这场出题十分阴间,后面几个乱搞题根本不会.jpg 赛时只过了5题,rk123,学校参加5/8. A. Alice and Bob [题意] 两人博弈,每次一个人从一 ...

最新文章

  1. linux xampp常见问题
  2. 【面试题】Ajax的原理和优缺点总结
  3. PyTorch学习问题记录
  4. Wing IDE 4.1破解教程
  5. java实现加减乘除运算符随机生成十道题并判断对错_简单小程序——产生三十道小学四则运算题目...
  6. JDK 13:什么是AggressiveOpts?
  7. 【转】设置Win32窗口背景颜色
  8. 陈睿:架构设计之数据库拆分六大原则
  9. 开源PHP多应用授权系统源码
  10. 基于点特征的各位姿求解算法对比(pose-estimation-compared)
  11. 一次数学分析的教学(依然进行中)
  12. layui 汉字乱码_layui table中文乱码
  13. 【RDMA】19. RDMA之iWARP Soft-iWARP
  14. 前端生成唯一id UUID
  15. Android加载百度地图
  16. 程序员常秃顶?Python创始人笑了,防脱发还得学这门语言,不然干脆转行得了
  17. iOS之颜色的调用、显示16进制颜色、渐变色
  18. Wireshark中常见的TCP Info
  19. 【UnityUGUIShader】创建材质球Material代码控制更换物体材质球
  20. 【自己动手写CPU】除法指令的实现

热门文章

  1. 【STM32+机智云】机智云手机APP点灯实验踩坑记录
  2. 【Accumulation】The definition of SISR
  3. 电脑桌面怎么设置html背景图,css中如何设置背景图片?
  4. 【3】一铭操作系统初体验,安装ope…
  5. English Learning - Day6 作业打卡 2022.12.12 周一
  6. Matplotlib之扇形图绘制
  7. window7电脑声音图标不见了
  8. java学习书籍推荐
  9. 数据如何变成知识(3):提取暗数据
  10. 电视盒子 android tv6,电视盒子到底应该怎么选?掌握这5点就行了