NOI模拟20220519
清北营前的倒数第二场考试,仍然不太理想
开场看T1,很快就搞到了50pts,想要优化成正解,我很清楚正解的复杂度一定和本质不同的gcd个数有关
也想到了那个dp,但是我不会O(n)转移,于是祭了
然后看T2,wcnmd数据范围是这个??,SG函数,写状压?线性都不行,算啦算啦弃了,于是没写,考后被波波疯狂diss
T3确实是有一点眼熟哈,然而并没有下文了,只会\(n^4\),然鹅小小的把转移集中一下就是正解了
原题是陈雨昕的原创,复杂度是\(n^2\)的,用了概率的性质,对贡献进行拆分,每次转移平均值,是我见过的dp最登峰造极的了
T1 调兵遣将
就是那个DP数组可以O(n)求,然后每次提取关键点,复杂度就对了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=50005;
const int mod=998244353;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int n,w[N],ans[N],cnm;
struct BIT{
int tr1[N],tr2[N],lm;
void init(int x){lm=x;fo(i,1,lm)tr1[i]=tr2[i]=0;}
void insert(int x,int v){
for(int i=x;i<=lm;i+=(i&-i)){
tr1[i]=(tr1[i]+v)%mod;
tr2[i]=(tr2[i]+v*(x-1))%mod;
}
}
void ins(int l,int r,int v){
insert(l,v);insert(r+1,-v);
}
int query(int x){
int ret=0;
for(int i=x;i;i-=(i&-i))
ret=(ret+tr1[i]*x-tr2[i]+mod)%mod;
return ret;
}
int qry(int l,int r){return (query(r)-query(l-1)+mod)%mod;}
}pre,suf;
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
struct XDS{
#define ls x<<1
#define rs x<<1|1
int gd[N*4];
void pushup(int x){
gd[x]=gcd(gd[ls],gd[rs]);
}
void build(int x,int l,int r){
if(l==r)return gd[x]=w[l],void();
int mid=l+r>>1;
build(ls,l,mid);build(rs,mid+1,r);
pushup(x);
}
int queryr(int x,int l,int r,int ql,int v){
if(ql<=l){
if(gd[x]%v==0)return r;
if(l==r)return l-1;
int mid=l+r>>1;
if(gd[ls]%v==0)return queryr(rs,mid+1,r,ql,v);
else return queryr(ls,l,mid,ql,v);
}
int mid=l+r>>1,ret=0;
if(ql<=mid)ret=queryr(ls,l,mid,ql,v);
if(!ret||ret==mid)ret=queryr(rs,mid+1,r,ql,v);
return ret;
}
int queryl(int x,int l,int r,int qr,int v){
if(qr>=r){
if(gd[x]%v==0)return l;
if(l==r)return r+1;
int mid=l+r>>1;
if(gd[rs]%v==0)return queryl(ls,l,mid,qr,v);
else return queryl(rs,mid+1,r,qr,v);
}
int mid=l+r>>1,ret=0;
if(qr>mid+1)ret=queryl(rs,mid+1,r,qr,v);
if(!ret||ret==mid+1)ret=queryl(ls,l,mid,qr,v);
return ret;
}
#undef ls
#undef rs
}xds;
int lshw[N*30],lhw;
struct nodel{int l,r1,r2,w;};
bool cmpl(nodel a,nodel b){return a.l>b.l;}
vector<nodel> bl[N],ul[N*30];
struct noder{int l1,l2,r,w;};
bool cmpr(noder a,noder b){return a.r<b.r;}
vector<noder> br[N],ur[N*30];
int lsh[N*30],lh;
int f[N*30],pf[N*30],cf[N*30];
int g[N*30],pg[N*30],cg[N*30];
void sol(int x){
// cerr<<x<<" "<<lshw[x]<<endl;
lh=0;lsh[++lh]=n+1;
for(nodel i:ul[x])lsh[++lh]=i.l,lsh[++lh]=i.r1,lsh[++lh]=i.r2;
for(noder i:ur[x])lsh[++lh]=i.r,lsh[++lh]=i.l1,lsh[++lh]=i.l2;
sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;lh--;
for(nodel &i:ul[x]){
// cerr<<"ul"<<" "<<x<<" "<<i.l<<" "<<i.r1<<" "<<i.r2<<endl;
i.l=lower_bound(lsh+1,lsh+lh+1,i.l)-lsh;
// cerr<<i.l<<endl;
i.r1=lower_bound(lsh+1,lsh+lh+1,i.r1)-lsh;
i.r2=lower_bound(lsh+1,lsh+lh+1,i.r2)-lsh;
}
// cerr<<ul[x][0].l<<" "<<ul[x][1].l
for(noder &i:ur[x]){
// cerr<<"ur"<<" "<<x<<" "<<i.l1<<" "<<i.l2<<" "<<i.r<<endl;
i.r=lower_bound(lsh+1,lsh+lh+1,i.r)-lsh;
i.l1=lower_bound(lsh+1,lsh+lh+1,i.l1)-lsh;
i.l2=lower_bound(lsh+1,lsh+lh+1,i.l2)-lsh;
}
fo(i,0,lh+1)f[i]=cf[i]=pf[i]=g[i]=cg[i]=pg[i]=0;
f[0]=1;pf[0]=lsh[1];
g[lh+1]=1;pg[lh+1]=lsh[lh+1]-lsh[lh];
// cerr<<"CM"<<" "<<ul[x][1].l<<endl;
sort(ul[x].begin(),ul[x].end(),cmpl);
// cerr<<"SB"<<" "<<ul[x][0].l<<endl;
sort(ur[x].begin(),ur[x].end(),cmpr);
int it=0,sz=ur[x].size();
fo(i,1,lh){
while(it<sz&&ur[x][it].r==i){
// cerr<<ur[x][it].r<<endl;
f[i]=(f[i]+pf[ur[x][it].l2-1]-pf[ur[x][it].l1-1]+f[ur[x][it].l1-1]+mod)%mod;
it++;
}
f[i]=(f[i-1]+f[i])%mod;
// cerr<<f[i]<<" ";
pf[i]=(pf[i-1]+f[i]*(lsh[i+1]-lsh[i]))%mod;
}
// cerr<<endl;
it=0;sz=ul[x].size();
fu(i,lh,1){
// cerr<<ul[x][1].l<<endl;
while(it<sz&&ul[x][it].l==i){
g[i]=(g[i]+pg[ul[x][it].r1+1]-pg[ul[x][it].r2+1]+g[ul[x][it].r2+1]+mod)%mod;
it++;
}
g[i]=(g[i+1]+g[i])%mod;
// cerr<<g[i]<<" ";
pg[i]=(pg[i+1]+g[i]*(lsh[i]-lsh[i-1]))%mod;
}
// cerr<<endl;
fo(i,1,lh+1){
// cerr<<lsh[i]<<" "<<ans[lsh[i]]<<" "<<f[i-1]<<" "<<g[i]<<" ";
ans[lsh[i-1]+1]=(ans[lsh[i-1]+1]+f[i-1]*g[i])%mod;
ans[lsh[i]]=(ans[lsh[i]]-f[i-1]*g[i]%mod+mod)%mod;
// cerr<<ans[lsh[i]]<<endl;
if(i==lh+1)break;
ans[lsh[i]]=(ans[lsh[i]]+f[i-1]*g[i+1])%mod;
ans[lsh[i]+1]=(ans[lsh[i]+1]-f[i-1]*g[i+1]%mod+mod)%mod;
}
// fo(i,1,n)cerr<<ans[i]<<" ";cerr<<endl;
cnm=(cnm+f[lh])%mod;
}
signed main(){
n=read();
fo(i,1,n)w[i]=read();
xds.build(1,1,n);
fo(l,1,n){
int las=l,gg=w[l],now;
while(las<=n){
gg=gcd(gg,w[las]);lshw[++lhw]=gg;
now=xds.queryr(1,1,n,las,gg);
bl[l].emplace_back(nodel{l,las,now,gg});
las=now+1;
}
}
fo(r,1,n){
int las=r,gg=w[r],now;
while(las>=1){
gg=gcd(gg,w[las]);lshw[++lhw]=gg;
now=xds.queryl(1,1,n,las,gg);
br[r].emplace_back(noder{now,las,r,gg});
las=now-1;
}
}
sort(lshw+1,lshw+lhw+1);lhw=unique(lshw+1,lshw+lhw+1)-lshw-1;
fo(i,1,n){
for(nodel j:bl[i]){
int ps=lower_bound(lshw+1,lshw+lhw+1,j.w)-lshw;
ul[ps].push_back(j);
}
for(noder j:br[i]){
int ps=lower_bound(lshw+1,lshw+lhw+1,j.w)-lshw;
ur[ps].push_back(j);
}
}
fo(i,1,lhw)sol(i);
fo(i,1,n)ans[i]=(ans[i]+ans[i-1])%mod;
fo(i,1,n)ans[i]=(cnm-ans[i]+mod)%mod;
fo(i,1,n)printf("%lld ",ans[i]);
}
T2 一掷千金
似乎SG的题好像都能拆分,然后异或起来,这个也行,就是每个白点的异或
然后不知道如何证明白点的SG是啥,题解告诉我是lowbit(max(x,y))
于是我们可以运用牛逼线段树+扫描线,可是我们怎么维护lowbit的异或和呢
发现我们可以建立一颗完全二叉树的线段树,这样的话每个完整区间的答案可以O(1)得到,值域[0,1<<30)
这样我们就可以方便的维护答案了,注意我们是求矩形的并,而不是矩形的叠加,我们只能算有值的位置的答案...
线段树写起来非常的奇妙,体会一下,美丽的线段树!
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=1e5+5;
struct XDS{
struct POT{int ls,rs,sz,tg,as;}tr[N*80];
int seg;
void pushup(int x,int l,int r){
tr[x].sz=tr[tr[x].ls].sz+tr[tr[x].rs].sz;
if(tr[x].tg)tr[x].sz=r-l+1;
tr[x].as=tr[tr[x].ls].as^tr[tr[x].rs].as;
if(tr[x].tg)tr[x].as=(l==r?(l&-l):((l&-l)^((r-l>>1)+1)));
}
void ins(int &x,int l,int r,int ql,int qr,int v){
if(ql>qr)return ;
if(!x)x=++seg;
if(ql<=l&&r<=qr){
tr[x].tg+=v;
if(tr[x].tg)tr[x].sz=r-l+1;
else tr[x].sz=tr[tr[x].ls].sz+tr[tr[x].rs].sz;
if(tr[x].tg)tr[x].as=(l==r?(l&-l):((l&-l)^((r-l>>1)+1)));
else tr[x].as=tr[tr[x].ls].as^tr[tr[x].rs].as;
return ;
}
int mid=l+r>>1;
if(ql<=mid)ins(tr[x].ls,l,mid,ql,qr,v);
if(qr>mid)ins(tr[x].rs,mid+1,r,ql,qr,v);
pushup(x,l,r);
}
int qsz(int x,int l,int r,int ql,int qr,int ok){
if(ql>qr)return 0;ok|=tr[x].tg;
if(ql<=l&&r<=qr)return ok?r-l+1:tr[x].sz;
int mid=l+r>>1,ret=0;
if(ql<=mid)ret+=qsz(tr[x].ls,l,mid,ql,qr,ok);
if(qr>mid)ret+=qsz(tr[x].rs,mid+1,r,ql,qr,ok);
pushup(x,l,r);return ret;
}
int qas(int x,int l,int r,int ql,int qr,int ok){
if(ql>qr)return 0;ok|=tr[x].tg;
if(ql<=l&&r<=qr)return ok?(l==r?(l&-l):((l&-l)^((r-l>>1)+1))):tr[x].as;
int mid=l+r>>1,ret=0;
if(ql<=mid)ret^=qas(tr[x].ls,l,mid,ql,qr,ok);
if(qr>mid)ret^=qas(tr[x].rs,mid+1,r,ql,qr,ok);
pushup(x,l,r);return ret;
}
}xds;
int T,n,m,rt,ans;
struct line{int ya,yb;};
vector<line> add[N],del[N];
signed main(){
T=read();n=read();m=read();
fo(i,1,T){
int xa=read(),ya=read(),xb=read(),yb=read();
add[xa].push_back(line{ya,yb});
del[xb+1].push_back(line{ya,yb});
}
fo(i,1,n){
for(line x:add[i])xds.ins(rt,0,(1<<30)-1,x.ya,x.yb,1);
for(line x:del[i])xds.ins(rt,0,(1<<30)-1,x.ya,x.yb,-1);
if(xds.qsz(rt,0,(1<<30)-1,0,i,0)&1)ans^=(i&-i);
ans^=xds.qas(rt,0,(1<<30)-1,i+1,m,0);
// cerr<<ans<<endl;
}
printf("%lld\n",ans);
}
T3 树拓扑序
吐槽:出题人这是啥垃圾拓扑序,反着的???
第一反应,拆分一下,拆成每一个顺序对对答案的贡献,发现这个可以直接枚举然后在LCA处合并一下,复杂度\(O(n^4)\)
并不能通过,于是我们观察这个式子,发现可以枚举x,跳的同时统计所有大于x的方案数(后缀和),于是就可以通过了
那个\(O(n^2)\)的做法是这样的,我们定义f[u][v]表示u子树和v子树合并起来的逆序对平均数
我们转移的时候可以拆分成u的几个儿子和v合并的答案加上u和v合并的答案,几个儿子的答案由另外的数组得出,剩下的直接看是u在第一位还是v在第一位就好了...
AC_code
#include<bits/stdc++.h>
using namespace std;
// #define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return s*t;
}
const int N=505;
const int mod=1e9+7;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=1ll*ret*x%mod;
x=1ll*x*x%mod;y>>=1;
}return ret;
}
int n,ans;
vector<int> e[N];
int jc[N],inv[N];
int C(int x,int y){return 1ll*jc[x]*inv[y]%mod*inv[x-y]%mod;}
int D(int x,int y){return 1ll*inv[x]*jc[y]%mod*jc[x-y]%mod;}
int fa[N],dep[N],sz[N];
int dp[N][N][N],g[N],ig[N];
void dfs_pre(int x,int f){
fa[x]=f;dep[x]=dep[f]+1;
for(int y:e[x])if(y!=f)dfs_pre(y,x);
}
void dfs_g(int x,int f){
sz[x]=0;g[x]=1;
for(int y:e[x])if(y!=f){
dfs_g(y,x);sz[x]+=sz[y];
g[x]=1ll*g[x]*g[y]%mod*C(sz[x],sz[y])%mod;
}sz[x]++;
}
int h[N];
void get_dp(int v){
dp[v][v][1]=g[v];int cv=v;
while(fa[v]){
int x=fa[v],now=0;
fo(i,1,sz[x])h[i]=0;h[0]=1;
for(int y:e[x])if(y!=fa[x]&&y!=v){
fu(i,now,0){
fo(j,1,sz[y]){
h[i+j]=(h[i+j]+1ll*h[i]*C(i+j,j)%mod*C(now-i+sz[y]-j,sz[y]-j)%mod*g[y])%mod;
}
h[i]=1ll*h[i]*C(now-i+sz[y],sz[y])%mod*g[y]%mod;
}
now+=sz[y];
}
fo(i,1,sz[v])if(dp[cv][v][i]){
fo(j,i,i+now){
dp[cv][x][j+1]=(dp[cv][x][j+1]+1ll*dp[cv][v][i]*h[j-i]%mod*C(j-1,i-1)%mod*C(sz[x]-1-j,now-j+i))%mod;
}
}
v=fa[v];
}
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
while(dep[x]>dep[y])x=fa[x];
while(x!=y)x=fa[x],y=fa[y];
return x;
}
int tp[N],bh[N][N][N];
int sol(int x,int y){
int lca=LCA(x,y);
if(lca==x)return g[1];
else if(lca==y)return 0;
int nx=x,ny=y,ret=0;
while(fa[nx]!=lca)nx=fa[nx];
while(fa[ny]!=lca)ny=fa[ny];
fo(i,1,sz[nx])if(dp[x][nx][i]){
fo(k,0,sz[ny]-1)tp[k]=1ll*dp[x][nx][i]%mod*C(k+i-1,i-1)%mod*C(sz[nx]-i+sz[ny]-k,sz[nx]-i)%mod;
fo(k,1,sz[ny]-1)tp[k]=(tp[k]+tp[k-1])%mod;
fo(j,1,sz[ny])if(dp[y][ny][j]){
ret=(ret+1ll*tp[j-1]*dp[y][ny][j])%mod;
}
}
int now=1ll*g[lca]*D(sz[lca],sz[nx])%mod*D(sz[lca]-sz[nx],sz[ny])%mod*ig[nx]%mod*ig[ny]%mod;
ret=1ll*ret*now%mod*C(sz[lca],sz[nx]+sz[ny])%mod;
while(fa[lca]){
int x=fa[lca];
ret=1ll*ret*g[x]%mod*ig[lca]%mod;
lca=fa[lca];
}
return ret;
}
int work(int x){
int ret=0,res=0,nx=x;
fo(y,x+1,n)if(LCA(x,y)==x)res=(res+g[x])%mod;
while(fa[nx]){
int f=fa[nx],tmp=0;
res=1ll*res*g[f]%mod*ig[nx]%mod;
for(int ny:e[f])if(ny!=fa[f]&&ny!=nx){
int ttm=0;
fo(i,1,sz[nx])if(dp[x][nx][i]){
fo(k,0,sz[ny]-1)tp[k]=1ll*dp[x][nx][i]%mod*C(k+i-1,i-1)%mod*C(sz[nx]-i+sz[ny]-k,sz[nx]-i)%mod;
fo(k,1,sz[ny]-1)tp[k]=(tp[k]+tp[k-1])%mod;
fo(j,1,sz[ny])if(bh[x+1][ny][j]){
ttm=(ttm+1ll*tp[j-1]*bh[x+1][ny][j])%mod;
}
}
int now=1ll*g[f]*D(sz[f],sz[nx])%mod*D(sz[f]-sz[nx],sz[ny])%mod*ig[nx]%mod*ig[ny]%mod;
ttm=1ll*ttm*now%mod*C(sz[f],sz[nx]+sz[ny])%mod;
res=(res+ttm)%mod;
}nx=f;
}
return res;
}
signed main(){
n=read();
jc[0]=1;fo(i,1,n)jc[i]=1ll*jc[i-1]*i%mod;
inv[0]=1;inv[n]=ksm(jc[n],mod-2);
fu(i,n-1,1)inv[i]=1ll*inv[i+1]*(i+1)%mod;
fo(i,1,n-1){
int x=read(),y=read();
e[x].push_back(y);e[y].push_back(x);
}
dfs_pre(1,0);dfs_g(1,0);
fo(i,1,n)ig[i]=ksm(g[i],mod-2);
fo(i,1,n)get_dp(i);
fu(i,n,1)fo(j,1,n)fo(k,1,n)bh[i][j][k]=(bh[i+1][j][k]+dp[i][j][k])%mod;
// cerr<<"SB"<<" "<<1.0*clock()/CLOCKS_PER_SEC<<endl;
// fo(i,1,n)fo(j,i+1,n)ans=(ans+sol(i,j))%mod;
fo(i,1,n)ans=(ans+work(i))%mod;
printf("%d\n",ans);
return 0;
}