【考试总结】2022-01-21
高中数列问题
Let \(p_i\) be \(\lfloor\frac {i+b}c\rfloor,sf_i\) be \(\sum_{i=1}^n,q_j\) be the first k whose \(p_k=j\),which is found to be \(\rm jC-B\)
\[\begin{aligned}a[n]-A&=\sum_{i=1}^n f_{i}a[p_i]\\ a[n]-A&=\sum_{i=1}^n f_i\left(A+\sum_{j=1}^{p_i}f_ja[p_j]\right)\\ a[n]-A&=Asf_n+\sum_{i=1}^{p_n}f_ia[p_i]\sum_{k=q_i}^n f_k\\ a[n]-A-Asf_n&=\sum_{i=1}^{p_n}f_ia[p_i]\left(sf_n-sf_{q_i-1}\right) \end{aligned}\]By Using Lagrange interpolation,it's easy to calculate \(sf_n\) for any n in \(\Theta(Deg)\) 's time
Define \(G_i=f_i(sf_i-sf_{q_i})\) Here we get a \(2m+1\) degree polynomial in the subProblem
With Similar deduction we can get the following formula
\[\sum_{i=1}^n G(i)a[p_i]=AsG_n+\sum_{i=1}^{p_n}f_ia[p_i](sg_n-sg_{q_i-1}) \]\[G'[i]=f_i(sg_{n}-sg_{q_i-1}) \]Attention here \(q_i-1\) is a primary function of \(i\),so Lagrange interpolation is available here
The degree of the polynomial increases by \(m+1\) in a recursion so overall the complexity is \(\Theta(m^2\log^3n)\)
Code Display
const int N=1e4+10;
int fac[N],ifac[N],pre[N],poly[N],suf[N],A,B,C,n,m;
inline int lagrange(int x,int *y,int deg){
if(x<0) return 0;
if(x<=deg+1) return y[x];
pre[0]=suf[deg+2]=1;
rep(i,1,deg+1) pre[i]=mul(pre[i-1],(mod+(x-i)%mod)%mod);
Down(i,deg+1,1) suf[i]=mul(suf[i+1],((x-i)%mod+mod)%mod);
int ans=0;
rep(i,1,deg+1){
int prod=mul(pre[i-1],mul(suf[i+1],mul(ifac[deg-i+1],ifac[i-1])));
if((deg-i)&1) ckadd(ans,mul(y[i],prod));
else ckdel(ans,mul(y[i],prod));
}
return ans;
}
inline int GetF(int x){
int res=0;
Down(i,m,0) res=add(mul(res,x),poly[i]);
return res;
}
int y[N],tmp[N],sg[N];
inline int Q(int x){return C*x-B;}
inline int solve(int n,int deg){
if(n<=deg+1){
vector a={A};
for(int i=1;i<=n;++i) a.push_back(add(a.back(),mul(a[(i+B)/C],GetF(i))));
int ans=0;
rep(i,1,n) ckadd(ans,mul(del(sg[i],sg[i-1]),a[(i+B)/C]));
return ans;
}
int New=deg+m+1,sgn=lagrange(n,sg,deg+1);
for(int i=1;i<=2+New;++i) if(Q(i)<=n) tmp[i]=mul(GetF(i),del(sgn,lagrange(Q(i)-1,sg,deg+1)));
rep(i,1,New+2) sg[i]=add(sg[i-1],tmp[i]),tmp[i]=0;
return add(mul(A,sgn),solve((n+B)/C,New));
}
signed main(){
freopen("seq.in","r",stdin); freopen("seq.out","w",stdout);
n=10000; fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],mod-2);
Down(i,n,1) ifac[i-1]=mul(ifac[i],i);
n=read(); A=read(); B=read(); C=read(); m=read();
rep(i,0,m) poly[i]=read();
vector a={A}; a.resize(1000);
for(int i=1;i<=100;++i) a[i]=add(a[i-1],mul(a[(i+B)/C],GetF(i)));
rep(i,1,m+2) sg[i]=add(sg[i-1],GetF(i));
print(add(A,solve(n,m)));
return 0;
}
机动车限行
以下展开的讨论以 \(1\) 为例,因为对于颜色 \(2,3\) 而言除数字不同之外没有任何差别
考虑将 \(2\) 号点去掉之后产生了若干连通块,删掉 \(3\) 号点之后原图产生若干连通块,去除不含有 \(1\) 号点的连通块之后分别将其重标号
此时对于同时存在于连通块 \(p,q\) 的点 \(i\) 就在 \(p,q\) 之间连一条边,不难发现现在图是一张二分图
现在的问题时使用选择最小的边覆盖所有的点,答案时点数-最大匹配数
方案的构造就是选择所有的匹配边,对于非匹配点随便找一条出边,由于不再能找到奇数长度增广路,那么找哪条是没有区别的
Code Display
const int N=1e5+10;
int m,n,dep[N],S,T,tot,head[N],cnt,cur[N];
struct edge{int to,nxt,lim,id;}e[N<<1];
inline void adde(int u,int v,int w,int id){
e[++cnt]={v,head[u],w,id}; head[u]=cnt;
return ;
}
inline void add(int u,int v,int w,int id){return adde(u,v,w,id),adde(v,u,0,id);}
inline bool bfs(){
for(int i=1;i<=tot;++i) cur[i]=head[i],dep[i]=0; dep[S]=1;
queue q; q.push(S);
while(q.size()){
int fr=q.front(); q.pop();
for(int i=head[fr];i;i=e[i].nxt) if(e[i].lim){
int t=e[i].to; if(dep[t]) continue;
q.push(t); dep[t]=dep[fr]+1;
}
}
return dep[T];
}
inline int dfs(int x,int in){
if(x==T) return in; int out=0;
for(int i=cur[x];i;cur[x]=i,i=e[i].nxt)if(e[i].lim){
int t=e[i].to; if(dep[x]!=dep[t]-1) continue;
int res=dfs(t,min(in,e[i].lim));
in-=res; out+=res; e[i].lim-=res; e[i^1].lim+=res;
if(!in) break;
} if(!out) dep[x]=0;
return out;
}
int id[3][N],typ[N];
struct DSU{
int fa[N],mark[N];
inline void init(){rep(i,1,n) mark[i]=0,fa[i]=i; return ;}
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y){return fa[find(x)]=find(y),void();}
}Dsu[3];
vector g[N];
bool vis[N];
inline void solve(int Id){
int alr=0; tot=0; cnt=1; S=++tot; T=++tot;
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
int num[3]={};
for(int ban:{1,2,3}) if(Id!=ban){
Dsu[++alr].init();
rep(i,1,n) if(typ[i]!=ban){
for(auto t:g[i]) if(typ[t]!=ban) Dsu[alr].merge(i,t);
}
rep(i,1,n) if(typ[i]==Id) Dsu[alr].mark[Dsu[alr].find(i)]++;
rep(i,1,n) if(Dsu[alr].mark[i]){
id[alr][i]=++tot;
if(alr==1) add(S,tot,1,0);
else add(tot,T,1,0);
}
num[alr]=tot;
}
for(int i=1;i<=n;++i) if(typ[i]==Id) add(id[1][Dsu[1].find(i)],id[2][Dsu[2].find(i)],1,i);
int ans=0;
while(bfs()) ans+=dfs(S,0x3f3f3f3f3f3f3f3f);
print(tot-2-ans);
for(int i=head[S];i;i=e[i].nxt) if(e[i].lim==0) vis[e[i].to]=1;
for(int i=head[T];i;i=e[i].nxt) if(e[i].lim) vis[e[i].to]=1;
for(int i=3;i<=num[1];++i) if(vis[i]){
for(int j=head[i];j;j=e[j].nxt) if(!e[j].lim) print(e[j].id);
}else print(e[head[i]].id);
for(int i=num[1]+1;i<=num[2];++i) if(!vis[i]) print(e[head[i]].id); puts("");
return ;
}
signed main(){
freopen("restriction.in","r",stdin); freopen("restriction.out","w",stdout);
n=read(); m=read(); rep(i,1,n) typ[i]=read();
for(int i=1;i<=m;++i){
int u=read(),v=read();
g[u].pb(v); g[v].pb(u);
}
solve(1); solve(2); solve(3);
return 0;
}
城市绿化
我的做法可以通过所有数据,并且我自己有理论上的 hack 方法:
找到两种平方算法:子树剥离和层间询问,层间点数小于 \(50\) 时跑层间询问,否则跑子树剥离
在子树剥离的过程中有些底层优化,利用的时都没有被确定的点和已经被确定的点的 \(\rm LCA\) 的深度信息来减少赘余,但是完全有可能时负优化就不瞎写了
一种可能的 hack 方法时将树分成 \(1000\) 个大小为 \(100\) 的块,造成卡子树剥离的树的形态,那么每个深度大的点要逐块询问次数比较多些
EOF
一种标算也是求出所有点的深度之重链剖分,根号次重构,每次询问一个新点的父亲逐个链底找 \(\rm LCA\),最多跳 \(\log\) 次,两次重构之间随机剖分
注意到加入的点超过 \(\sqrt n\) 个就会重构,而这些可能不满足的点都被压到了很少的深度,所以是正确的
而套用 WC2018即时战略做法 的做法询问次数明显比我的做法的询问次数要小得多
Code Display
#include "green.h"
const int N=1e5+10;
int bz[N][20],dep[N],fa[N];
inline bool insub(int x,int y){
if(dep[x]>dep[y]) swap(x,y);
return visit(x,y)==dep[y]-dep[x];
}
vector son[N];
inline int lca(int x,int y){
if(dep[x]=dep[y]) x=bz[x][i];
if(x==y) return x;
for(int i=19;~i;--i) if(bz[x][i]!=bz[y][i]) x=bz[x][i],y=bz[y][i];
return bz[x][0];
}
inline void init(int x){
bz[x][0]=fa[x];
for(int i=1;bz[x][i-1];++i) bz[x][i]=bz[bz[x][i-1]][i-1];
return ;
}
inline int jump(int x,int dst){
for(int i=19;~i;--i) if(dst>>i&1) x=bz[x][i];
return x;
}
inline void link(int x,int y){
if(dep[x]>dep[y]) swap(x,y);
son[fa[y]=x].push_back(y);
init(y);
return ;
}
int alr[N];
inline int getanc(int x,int tar){return jump(x,dep[x]-tar);}
inline void solve(vector > vec,int dmn,int dmx){
if(dmx==dmn) return ;
if(dmx-dmn==1){
for(auto t:vec[1]) link(vec[0][0],t);
return ;
} // father of nodes with depth of dmn has been calculated
int dnow=dmn+1;
for(auto node:vec[1]) link(node,vec[0][0]);
if(vec[dnow-dmn].size()==1) while(dnow > undecide;
rep(i,dnow+1,dmx){
for(auto t:vec[i-dmn]) alr[t]=vec[0][0],undecide.insert(make_pair(i,t));
}
vector > > tmp;
tmp.resize(vec[dnow-dmn].size());
map mp,app;
int siz=vec[dnow-dmn].size();
rep(i,0,siz-1) mp[vec[dnow-dmn][i]]=i,tmp[i].push_back({vec[dnow-dmn][i]});
for(auto t:vec[dnow+1-dmn]) if(undecide.count({dnow+1,t})){
while(dep[alr[t]] >::iterator it=undecide.begin();
for(;it!=undecide.end();){
int t2=it->sec;
if(lca(alr[t2],alr[t])!=alr[t2]){++it; continue;}
int lcadep=(dep[t]+dep[t2]-visit(t,t2))/2;
if(lcadep>=dnow){
if(tmp[kk].size()<=it->fir-dnow) tmp[kk].push_back({it->sec});
else tmp[kk][it->fir-dnow].push_back(it->sec);
++it; undecide.erase(prev(it));
}else{
int ee=getanc(alr[t],lcadep);
int tar=son[ee][getanc(alr[t],lcadep+1)==son[ee][0]];
if(dep[tar]>dep[alr[t2]]) alr[t2]=tar;
++it;
}
}
}
rep(i,0,siz-1) solve(tmp[i],dnow,dnow+tmp[i].size()-1);
return ;
}
vector > Dep[2];
vector nds;
void findtree(int n,int m,int *p){
if(n==1) return ;
rep(i,2,n) if((dep[i]=visit(i,1))==1) fa[i]=1,nds.push_back(i);
int dmx=0;
rep(i,2,n) ckmax(dmx,dep[i]);
cerr<