A
枚举每个矩形,前缀和优化即可。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 2000+5;
char str[MAXN];
int n,k;
int sm[MAXN][MAXN];
int main(){
scanf("%d%d",&n,&k);
FOR(i,1,n){
scanf("%s",str+1);
FOR(j,1,n){
sm[i][j] = sm[i][j-1]+sm[i-1][j]-sm[i-1][j-1]+str[j]-'0';
}
}
int ans = 0;
FOR(i,k,n){
FOR(j,k,n){
ans = std::max(ans,sm[i][j]-sm[i-k][j]-sm[i][j-k]+sm[i-k][j-k]);
}
}
printf("%d\n",ans);
return 0;
}
B
$O(n2^n)$ 可以做任意图,只需要考虑如何判断一个图是强连通图:有一个点在正图和反图上都能到达所有点。
这个题要注意到竞赛图缩点后 DAG 上拓扑序小的点向拓扑序大的点都有连边。
所以我们如果得到了一个集合 $S$ 是强连通的,设属于它的所有的点的出边的交为 $T$,对于任意 $R \subset T,R \neq \emptyset$,一定 $S | R$ 是不合法的区间。而我们发现这样顶多会不重不漏遍历每个状态一次,所以均摊复杂度是 $O(2^n)$ 的。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 24+1;
int n;
int e[MAXN],out[(1<<MAXN)+3],pc[(1<<MAXN)+3];
#define lowbit(x) ((x)&(-(x)))
bool f[(1<<MAXN)+3];
inline void Solve(){
scanf("%d",&n);CLR(e,0);CLR(f,0);
FOR(i,0,n-1){
FOR(j,0,n-1){
int x;scanf("%d",&x);
e[i] |= (x<<j);
}
}
out[0] = (1<<n)-1;
FOR(S,1,(1<<n)-1) out[S] = out[S-lowbit(S)]&e[pc[lowbit(S)]];
int ans = 1;
FOR(S,1,(1<<n)-1){
if(!f[S]){
ans++;
for(int T = out[S];T;T = (T-1)&out[S]){
f[S|T] = 1;
}
}
}
printf("%d\n",ans);
}
int main(){
FOR(i,0,23) pc[1<<i] = i;
int T;scanf("%d",&T);
while(T--) Solve();
return 0;
}
C
我们先来观察一下什么情况是不合法的。我们按轮考虑:
设数字 $i$ 在三个人的序列出现的位置分别是 $pa_i,pb_i,pc_i$;设在第 $i$ 轮三个人选的数字是 $x,y,z$,合法当且仅当
- $x,y,z$ 互不相同
- $x$ 在 $B,C$ 的喜好序列出现的位置在 $y,z$ 后面,其余同理
所以我们发现我们如果确定了这三个人选什么数的话, C 的排列的方案数都是不变的,可以设 $g_{i,j}$ 表示考虑确定了 C 的前 $i$ 位,已经进行了 $j$ 轮的方案数,转移可以枚举这一位是否是 C 最终选的数字中的一个位置,如果是就 $g_{i,j} = g_{i-1,j-1}(2j-(i-j))$,否则就直接 $g_{i,j} = g_{i-1,j}$。
然后我们现在就是要对有多少种不同的选数方案进行dp;设 $f_{i,j,k}$ 表示 A 选了它自己的序列中第 $i$ 个,B 选了第 $j$ 个,C 还空着 $k$ 个的方案数。枚举下一次选的位置 $i',j'$,我们发现首先 $i'$ 在 B 中出现的位置不能在 $j'$ 之前,$j'$ 在 A 中出现的位置不能在 $i'$ 之前。如果在 $[i,i'],[j,j']$ 有个数字没有被选过,一定是被 $C$ 选了,我们设这样的数字数量为 $x$。转移就是 $f_{i,j,k} \to k^{\underline x}f_{i',j',k+1-x}$。
这样复杂度就是 $O(n^6)$ 的。
首先我们可以预处理找 $x$ 的过程,复杂度变成了 $O(n^5)$。
我们考虑每次可以不用一起枚举 $i',j'$ ,可以设 $f1,f2$ 表示两个人的 dp 数组,所以复杂度变成了 $O(n^4)$。
我们发现每次只需要考虑 $i+1$ 的位置是否选择,选择了就换到另一个人的 dp 数组转移,复杂度 $O(n^3)$。注意到我们从第一个人转移到第二个人不用考虑任何限制,在第二个人转移回去的时候统一考虑即可。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 400+5;
const int ha = 1e9 + 7;
int n,a[MAXN],b[MAXN],pa[MAXN],pb[MAXN];
int f1[MAXN][MAXN][MAXN],f2[MAXN][MAXN][MAXN];
// A选了第i个,B选了第j个,C还能用k个
int g[MAXN][MAXN];
// 前i个 选了j个
inline void add(int &x,int y){
x += y;if(x >= ha) x -= ha;
}
int main(){
scanf("%d",&n);
FOR(i,1,n) scanf("%d",a+i),pa[a[i]] = i;
FOR(i,1,n) scanf("%d",b+i),pb[b[i]] = i;
f1[1][1][1] = 1;
FOR(i,1,n+1){
FOR(j,1,n+1){
FOR(k,0,n/3){
if(f1[i][j][k]){
if(k && pb[a[i+1]] > j) add(f1[i+1][j][k-1],1ll*f1[i][j][k]*k%ha);
if(pb[a[i+1]] <= j) add(f1[i+1][j][k],f1[i][j][k]);
add(f2[i+1][j][k],f1[i][j][k]);
}
if(f2[i][j][k]){
if(k && pa[b[j+1]] > i) add(f2[i][j+1][k-1],1ll*f2[i][j][k]*k%ha);
if(pa[b[j+1]] <= i) add(f2[i][j+1][k],f2[i][j][k]);
if(i == n+1 || j == n+1 || (pb[a[i]] > j+1 && pa[b[j+1]] > i)) add(f1[i][j+1][k+1],f2[i][j][k]);
}
}
}
}
g[0][0] = 1;
FOR(i,0,n-1){
FOR(j,0,n/3){
add(g[i+1][j+1],g[i][j]);
if(3*j-i > 0) add(g[i+1][j],1ll*g[i][j]*(3*j-i)%ha);
}
}
int ans = 1ll*f1[n+1][n+1][1]*g[n][n/3]%ha;
printf("%d\n",ans);
return 0;
}
D
树上路径问题如果不会了可以考虑链分治,分开维护重链和轻边。
对于重链的颜色直接用线段树维护区间赋值即可。
轻边的颜色有两种可能改变:
- 有一次修改经过了这个轻边
- 有一次修改经过了轻边的两个端点之一,并没有经过轻边
我们只需要知道这两个操作最后一次的时间,就能判断出轻边的颜色,线段树维护即可。
要注意是轻边的两个端点都要查,因为可能有某个修改的 lca 到父亲的边是轻边的情况。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 3e5 + 5;
int n,q;
struct Edge{
int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt;
inline void add(int u,int v){
e[++cnt] = (Edge){v,head[u]};head[u] = cnt;
e[++cnt] = (Edge){u,head[v]};head[v] = cnt;
}
#define lc ((x)<<1)
#define rc ((x)<<1|1)
int sm[MAXN<<2],ts[MAXN<<2],cov[MAXN<<2],tag[MAXN<<2];
// 0表示染黑 数字表示有个时间
inline void cover1(int x,int l,int r,int d){
sm[x] = (r-l+1)*d;cov[x] = d;
}
inline void cover2(int x,int l,int r,int d){
ts[x] = tag[x] = d;
}
inline void pushdown(int x,int l,int r){
int mid = (l + r) >> 1;
if(cov[x] != -1){
cover1(lc,l,mid,cov[x]);
cover1(rc,mid+1,r,cov[x]);
cov[x] = -1;
}
if(tag[x] != -1){
cover2(lc,l,mid,tag[x]);
cover2(rc,mid+1,r,tag[x]);
tag[x] = -1;
}
}
inline void modify(int x,int l,int r,int L,int R,int d){
if(L > R) return;
if(l == L && r == R){
cover1(x,l,r,d?0:1);
if(d) cover2(x,l,r,d);
return;
}
int mid = (l + r) >> 1;pushdown(x,l,r);
if(R <= mid) modify(lc,l,mid,L,R,d);
else if(L > mid) modify(rc,mid+1,r,L,R,d);
else modify(lc,l,mid,L,mid,d),modify(rc,mid+1,r,mid+1,R,d);
sm[x] = sm[lc]+sm[rc];
}
inline void modify2(int x,int l,int r,int L,int R,int d){
if(L > R) return;
if(l == L && r == R){
cover2(x,l,r,d);
return;
}
int mid = (l + r) >> 1;pushdown(x,l,r);
if(R <= mid) modify2(lc,l,mid,L,R,d);
else if(L > mid) modify2(rc,mid+1,r,L,R,d);
else modify2(lc,l,mid,L,mid,d),modify2(rc,mid+1,r,mid+1,R,d);
sm[x] = sm[lc]+sm[rc];
}
inline int query(int x,int l,int r,int L,int R){
if(L > R) return 0;
if(l == L && r == R) return sm[x];
int mid = (l + r) >> 1;pushdown(x,l,r);
if(R <= mid) return query(lc,l,mid,L,R);
if(L > mid) return query(rc,mid+1,r,L,R);
return query(lc,l,mid,L,mid)+query(rc,mid+1,r,mid+1,R);
}
inline int queryt(int x,int l,int r,int p){
if(l == r) return ts[x];
int mid = (l + r) >> 1;pushdown(x,l,r);
if(p <= mid) return queryt(lc,l,mid,p);
else return queryt(rc,mid+1,r,p);
}
inline void build(int x,int l,int r){
sm[x] = r-l+1;tag[x] = cov[x] = -1;
if(l == r) return;
int mid = (l + r) >> 1;
build(lc,l,mid);build(rc,mid+1,r);
}
int dfn[MAXN],tp[MAXN],son[MAXN],dep[MAXN],fa[MAXN],sz[MAXN],_;
inline void dfs1(int v){
dep[v] = dep[fa[v]]+1;sz[v] = 1;
for(int i = head[v];i;i = e[i].nxt){
if(e[i].to == fa[v]) continue;
fa[e[i].to] = v;dfs1(e[i].to);
sz[v] += sz[e[i].to];
son[v] = sz[son[v]] > sz[e[i].to] ? son[v] : e[i].to;
}
}
inline void dfs2(int v,int tp=1){
dfn[v] = ++_;::tp[v] = tp;
if(son[v]) dfs2(son[v],tp);
for(int i = head[v];i;i = e[i].nxt){
if(e[i].to == fa[v] || e[i].to == son[v]) continue;
dfs2(e[i].to,e[i].to);
}
}
int tim[MAXN];
inline void modify(int x,int y){
if(son[x]) modify(1,1,n,dfn[son[x]],dfn[son[x]],0);
if(son[y]) modify(1,1,n,dfn[son[y]],dfn[son[y]],0);
while(tp[x] != tp[y]){
if(dep[tp[x]] < dep[tp[y]]) std::swap(x,y);
if(son[tp[x]]) modify(1,1,n,dfn[son[tp[x]]],dfn[x],_);
modify2(1,1,n,dfn[tp[x]],dfn[tp[x]],_);
tim[tp[x]] = _;
x = fa[tp[x]];
if(son[x]) modify(1,1,n,dfn[son[x]],dfn[son[x]],0);
}
if(dep[x] > dep[y]) std::swap(x,y);
modify(1,1,n,dfn[x],dfn[y],_);
modify(1,1,n,dfn[x],dfn[x],0);
}
inline int query(int x,int y){
int res = 0;
while(tp[x] != tp[y]){
if(dep[tp[x]] < dep[tp[y]]) std::swap(x,y);
if(son[tp[x]]) res += query(1,1,n,dfn[son[tp[x]]],dfn[x]);
if(tim[tp[x]] < std::max(queryt(1,1,n,dfn[fa[tp[x]]]),queryt(1,1,n,dfn[tp[x]]))) res++;
x = fa[tp[x]];
}
if(dep[x] > dep[y]) std::swap(x,y);
if(son[x]) res += query(1,1,n,dfn[son[x]],dfn[y]);
return res;
}
int main(){
scanf("%d",&n);
FOR(i,2,n){
int u,v;scanf("%d%d",&u,&v);
add(u,v);
}
scanf("%d",&q);
dfs1(1);dfs2(1);build(1,1,n);
FOR(i,1,n) tim[i] = -1;
FOR(i,1,q){
_ = i;int opt,x,y;scanf("%d%d%d",&opt,&x,&y);
// if(i == 2) FF = 1;
if(opt == 1) modify(x,y);
else printf("%d\n",query(x,y));
}
return 0;
}