「JOISC 2015 Day2」Keys
对于每个 (S_i,T_i) 排序,对于按时间从小到大排序后的每个东西进行分类讨论
如果第 (i) 个点的种类和第 (i-1) 个点的种类为:
-
((S_i,T_{i-1})) 那么可以直接关门,不用考虑
-
((S_i,S_{i-1})) 那么 (i-1) 对应的人必须有钥匙
-
((T_i,T_{i-1})) 那么 (i) 对应的人必须有钥匙
-
((T_i,S_{i-1})) 那么 (i) 和 (i-1) 对应的人必须有钥匙
以上内容自己想了出来但是一直搞给给在想如何给一个人建两个点分别代表S,T去做,最后当然也是没有想出来
设 (a_i) 为只让 (i) 获得钥匙时能得到的权值。
对于第二种和第三种情况,给对应的 (a_i) 加上对应的权值即可。
对于第四种情况,和 (a_i) 一起 dp
即可。
「JOISC 2014 Day2」邮戳拉力赛
非常牛的题目
主要思路:发现从左到右和从右到左,两种路径出现的次数是相同的,而且从右到左的路径只能与后面的从左到右的路径进行匹配,所以我们可以把从右到左的路径看成左括号,从左到右的路径看成右括号,直接对括号序 dp
即可....
「JOISC 2018 Day 1」帐篷
dp
,设 (f_{i,j}) 为 (i) 行 (j) 列的方案数
(f_{i,j}=f_{i-1,j}+4 imes j imes f_{i-1,j-1}+frac{j imes (j+1)}{2} imes f_{i-1,j-2}+(i-1) imes j imes f_{i-2,j-1})
全程在考虑怎么算空集的贡献从而华丽败北
「JOISC 2018 Day 2」最差记者 3
感觉很难的题目,然而 ( m color{black}{F}color{red}{roggy}) 说这是憨憨题,诶,这就是我与 isij 全球金牌之间的差距吗
设 (b_i) 为令 (i) 点移动一次所需要的距离(显然,这也是 (i) 点移动一次后与 (i+1) 点所拉开的距离)
如果 (d_i le b_i) 那么显然 (b_i=b_{i-1})
否则 (b_i=lceilfrac{d_i}{b_{i-1}} ceil imes b_{i-1})
发现不同的 (b_i) 仅有 (log) 中,而且同样的 (b_i) 分布在一个区间,所以可以直接预处理出来直接做。
「PA2019 Final」Floyd‐Warshall
感觉很难的题目,然而神仙们都说这是简单题
发现最短路上的两个点 ((u,v)),这个 ((u,v)) 能被假算法正确算出当且仅当:
-
((u,v)) 之间只有 (1) 条边。
-
有一个点 (k),满足 ((u,k)) 和 ((k,v)) 都能被表示
以上内容全部想出来了然而就是没想到传递闭包
上述内容用传递闭包做,加上 bitset
优化即可。
牛客练习赛66 F - 青蛙树
规定相同权值的点,在笛卡尔树上为祖父 - 右儿子的关系,这样就可以在笛卡尔树上搞一搞了
设 ([l,r]) 的最小值为 (mn) ,那么这个区间形如:
把每个被 (mn) 分割的区间的方案数求出来,通过上面的定义,可知不包含 (l) 或者 (r) 的区间在笛卡尔树上形如连续一段往右走的路径的左子树,通过预处理做出这个
至于 (mn) 本身的贡献可以用卡特兰数计算
考虑包含 (l) 的区间的方案数怎么计算,包含 (r) 的区间同理。
考虑从 (l) 跳上 (l,r) 的 (LCA) 的过程,如果每次跳是从左子树跳到父亲,那么父亲和它右儿子的贡献是在包含 (l) 的区间中,需要计算。
因为并没有跳到 (LCA),所以父亲和它右儿子的贡献肯定是满足限制的。注意 (LCA) 本身的贡献不能被算上去。
把上述过程用倍增做即可。
看不懂可以看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005, Log = 20, mod = 998244353;
int Cat[N], c[N], fac[N], inv[N];
int ksm(int b, int n) {
int res = 1;
while(n){
if(n & 1) res = 1ll * res * b % mod;
b = 1ll * b * b % mod;
n >>= 1;
}
return res;
}
int C(int n, int m){
if(n < 0 || m < 0 || n < m) return 0;
return 1ll * fac[n] * inv[m] % mod * inv[n-m] %mod;
}
void init(int n){
fac[0] = 1;
for(int i = 1; i <= n + n; ++i)
fac[i] = 1ll * fac[i-1] * i % mod;
// cout<<"fuck
";
inv[n + n] = ksm(fac[n + n], mod - 2);
/// cout<<"fuck_twice
";
for(int i = n + n - 1; i >= 0; --i)
inv[i]= 1ll * (i+1) * inv[i+1] % mod;
// cout<<"goushi?
";
for(int i = 1; i <= n; ++i)
Cat[i] = (C(i + i, i) - C(i + i, i-1) + mod) % mod;
Cat[0] = 1;
for(int i = 1; i <= n; ++i)
c[i] = 1ll * Cat[i] * ksm(Cat[i-1], mod-2) % mod;
}
struct CartesianTree{
int n,a[N],fa[N][20],g[N][20],d[N],val[N],sum[N],dep[N];
int rt,ls[N],rs[N],st[N],top;
void dfs2(int x){
// cout<<x<<" "<<ls[x]<<" "<<rs[x]<<endl;
sum[x] = 1ll * sum[fa[x][0]] * val[ls[x]] % mod;
//cout<<x<<" "<<ls[x]<<" "<<fa[x][0]<<" "<<sum[x]<<" "<<sum[fa[x][0]]<<endl;
for(int i = 1; i < Log; ++i)
fa[x][i] = fa[fa[x][i-1]][i-1],
g[x][i] = 1ll * g[x][i-1] * g[fa[x][i-1]][i-1] % mod;
if(ls[x]!=0)
g[ls[x]][0] = 1ll * c[d[x]] * val[rs[x]] % mod,
// cout<<x<<" "<<ls[x]<<endl,
dfs2(ls[x]);
if(rs[x]!=0)
g[rs[x]][0] = 1,
// cout<<x<<" "<<rs[x]<<endl,
dfs2(rs[x]);
}
void dfs1(int x,int fath){
fa[x][0]=fath; dep[x]=dep[fa[x][0]]+1;
if(ls[x]) dfs1(ls[x],x);
if(rs[x]) dfs1(rs[x],x);
if(rs[x] && a[x]==a[rs[x]]) d[x]=d[rs[x]]+1;
else d[x]=1;
val[x] = 1ll * c[d[x]] * val[ls[x]] % mod * val[rs[x]] % mod;
//cout<<x<<" "<<c[d[x]]<<" "<<endl;
}
void init(int *_a,int _n){
n=_n; st[top=0]=0;
for(int i=1;i<=n;++i){
a[i]=_a[i];
while(top && a[st[top]]>a[i]) ls[i]=st[top],--top;
if(top) rs[st[top]]=i;
st[++top]=i;
}
rt=st[1]; val[0]=1; dep[0]=0;
dfs1(rt,0);
g[rt][0]=1; sum[0]=1;
dfs2(rt);
}
pair<int,int> query(int u,int v){ // first -> ans, second -> LCA
int ans=c[d[u]];
if(rs[u]) ans = 1ll * ans * val[rs[u]] % mod;
//cout<<ans<<" "<<c[d[u]]<<" "<<val[rs[u]]<<" fuck
";
if(dep[u]<=dep[v]){
for(int i = Log - 1; i >= 0; --i)
if(dep[fa[v][i]] >= dep[u]) v = fa[v][i];
if(u == v) return {1,v};
}
else{
for(int i = Log - 1; i >= 0; --i)
if(dep[fa[u][i]] > dep[v]) ans = 1ll * ans * g[u][i] % mod, u = fa[u][i];
if(fa[u][0] == v) return {ans,v};
else ans = 1ll * ans * g[u][0] % mod, u = fa[u][0];
}
for(int i = Log - 1; i >= 0; --i)
if(fa[u][i] != fa[v][i]) ans = 1ll * ans * g[u][i] % mod, u = fa[u][i], v=fa[v][i];
return {ans,fa[u][0]};
}
}T,Tr;
int n,a[N],inv_sum[N];
signed main(){
freopen("shadow.in","r",stdin);
freopen("shadow.out","w",stdout);
ios_base::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i];
init(n);
// cout<<"gouba?
";
T.init(a,n); reverse(a+1,a+n+1);
Tr.init(a,n); reverse(a+1,a+n+1);
for(int i=1;i<=n;++i)
inv_sum[i]=ksm(T.sum[i],mod-2);
int q,l,r,u,v,ans;
pair<int,int> t1,t2;
cin>>q;
// for(int i=1;i<=n;++i)
// cout<<Tr.ls[i]<<" "<<Tr.rs[i]<<" "<<Tr.fa[i][0],cout<<endl;
while(q--){
cin>>l>>r; ans=1;
t1=T.query(l,r); t2=Tr.query(n-r+1,n-l+1);
u = t1.second; v = n-t2.second+1;
ans = 1ll * t1.first * t2.first % mod * T.sum[v] % mod * inv_sum[u] % mod * Cat[T.dep[v]-T.dep[u]+1] % mod;
// cout<<t1.first<<" "<<t2.first<<" "<<t1.second<<" "<<t2.second<<" "<<u<<" "<<v<<" "<<T.sum[v]<<" "<<inv_sum[u]<<endl;
cout<<ans<<endl;
}
return 0;
}
「PA2011」Journeys
这题没前面那么神仙,但不会就是不会。
考虑一个暴力做法,对起点做一遍 bfs
,bfs
一个点的时候把包含该点的区间弄出来,对连边区间内的所有点做 bfs
。
这个暴力的瓶颈显然是:1.如何把包含该点的区间弄出来 2.对连边区间内的所有点做 bfs
。
对于 1 可以直接线段树维护
对于 2 ,发现 bfs
的过程中每个点只会走一遍,所以用并查集来维护没有被走过的点即可。
#include<bits/stdc++.h>
using namespace std;
#define ls p<<1
#define rs p<<1|1
#define pb push_back
#define mp make_pair
#define fi first
#define se second
const int N=5e5+5,M=N<<2;
vector<int> e[M];
int rl[M],rr[M],vis[M],v[M];
void upt(int p,int l,int r,int ql,int qr,int k){
if(ql<=l && r<=qr) return v[p]=1,(void)(e[p].pb(k));
int mid=(l+r)>>1;
if(ql<=mid) upt(ls,l,mid,ql,qr,k);
if(mid+1<=qr) upt(rs,mid+1,r,ql,qr,k);
}
struct node{
int l,r,step;
node(){}
node(int _l,int _r,int _s){
l=_l;r=_r;step=_s;
}
};
queue<node> q;
void query(int p,int l,int r,int pos,int step){
if(v[p]){
v[p]=0;
for(int i=0;i<e[p].size();++i){
int Id=e[p][i];
if(!vis[Id]) vis[Id]=1,q.push(node(rl[Id],rr[Id],step));
}
}
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) query(ls,l,mid,pos,step);
else query(rs,mid+1,r,pos,step);
}
int n,dis[N],f[N];
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
void bfs(int s){
for(int i=1;i<=n+1;++i)
f[i]=i;
q.push(node(s,s,0));
while(!q.empty()){
node u=q.front(); q.pop();
//cout<<u.l<<" "<<u.r<<" "<<u.step<<endl;
for(int i=find(u.l);i<=u.r;i=find(i+1))
query(1,1,n,i,u.step+1),dis[i]=u.step,f[i]=i+1;
}
}
signed main(){
int m,s,tot=0;
cin>>n>>m>>s;
for(int i=1;i<=m;++i){
int l1,r1,l2,r2;
cin>>l1>>r1>>l2>>r2;
rl[++tot]=l2; rr[tot]=r2;
upt(1,1,n,l1,r1,tot);
rl[++tot]=l1; rr[tot]=r1;
upt(1,1,n,l2,r2,tot);
}
bfs(s);
for(int i=1;i<=n;++i)
cout<<dis[i]<<"
";
return 0;
}