骄傲 孔雀羽翎上的暗槽 从最肮脏裂缝开凿 被爱意和现实击倒 停止创造
test10
大清洁(clr)
枚举 \(m\),因为小于调和级数,所以可以暴力扫每个位置计算初始计划表每个位置填法有 \(1/2\) 种。问题只剩下怎么去重,从小到大算出每个 \(m\) 恰好的方案数,就好容斥了,是简单的减法。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
const int N=100005, P=998244353;
int n, ans, coel[N];
char str[N];
signed main() {
freopen("clr.in","r",stdin);
freopen("clr.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> (str+1);
up(m,1,n-1) if(n%m==0) {
int res=1;
up(i,1,m) {
bool flag=1;
up(j,1,n/m) if(str[(j-1)*m+i]=='.') flag=0;
if(flag) res=2*res%P;
}
(coel[m]+=res)%=P, (ans+=coel[m])%=P;
up(i,2,n/m) (coel[m*i]-=coel[m])%=P;
}
cout << (ans%P+P)%P << '\n';
return 0;
}
最长子串(str)
其实做过 P3590 并且记得做法,注意到多加几个肯定很容易满足条件,左右拿三个进去扫,然后反证是过程多但不难的。
但是来都来了我们再胡一个对脑子更稳定的做法,考虑 \(a_i=A_i-B_i,b_i=B_i-C_i,c_i=C_i-A_i\),其中 \(A/B/C\) 是前缀和,然后你可以从 \((a/b/c)_l\neq (a/b/c)_r\) 的 \(l\) 转移到 \(r\),发现记录答案最大的 \((a,b,c)\) 不同的三元组和其答案就可以惹。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
const int N=10000005;
int n, ans=1; char s[N];
void solve(int l) {
int res=0, C=0, B=0, S=0;
up(i,l,n) {
C+=(s[i]=='N');
B+=(s[i]=='O');
S+=(s[i]=='I');
if(C!=B&&B!=S&&C!=S) ans=max(ans,i-l+1);
}
}
signed main() {
freopen("str.in","r",stdin);
freopen("str.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> (s+1);
solve(1), solve(2), solve(3);
up(i,1,n/2) swap(s[i],s[n-i+1]);
solve(1), solve(2), solve(3);
cout << ans;
return 0;
}
熬鹰杯(cup)
sort(&a[1],&a[n]); -> sort(&a[1],&a[n+1]);
题目会自然形成连通块个场馆,我们考虑怎么对一个连通块进行操作。考虑给相交的连边,你可以每次摘除生成树的叶子,所以操作方式显然是按照连通块大小从小到大删除连通块,所以相等大小连通块带来一个 \(\prod_{siz=1}^n cnt[siz]!\) 的贡献,之后每个连通块是独立子问题。
我们考虑怎么对一个连通块计数,你要寻找不破坏连通性的删比赛顺序,这个好难记状态的,不妨时间倒流变成更好做的寻找不破坏连通性的加比赛顺序,暴力 dp 大概是一个什么 \(f[i][l,r]\) 表示考虑到 \(i\) 轮并集是 \([l,r]\) 的方案数,转移是扩展 \([l,r]\) 或者钦定用 \(\times (cnt[l,r]-i)\) 的贡献去增加 \(i\)。
这个第二种贡献方式太拉跨了,考虑一下怎么优化,先观察加入时没有扩展区间的比赛怎么贡献,找到第一个覆盖的时刻,从那时候开始随便找一个时间加入,哦那这个是弱化版的树上拓扑排序方案数,可以用 \(n!\prod\frac{1}{siz_u}\) 来计算,发现一个扩展的比赛跟上一个拓展的比赛树直接划开,子树大小的贡献刚好是 \(n-cnt[l_{pre}+1,r_{pre}-1]\),那么可以 dp 了,设 \(f[l,r]\) 表示并集为 \([l,r]\) 的拓扑系数,用类似前缀和的东西优化即可。
所以说有时候看着 dp 不知道怎么下手的时候不妨看看全局的贡献情况,心里更有底一点。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define ll long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
using namespace std;
const int N=4005, P=1e9+7;
int mul[N], inv[N], f[N][N], mid[N][N], cnt[N][N];
int L[N][N], R[N][N], s[N][N], Ans=1, ran[N], sav=1;
vector<int> lr[N], rl[N];
pii p[N];
inline void Add(int &a,int b) { a=(a+b)%P; }
inline void Mul(int &a,int b) { a=(ll)a*b%P; }
void solve(int n) {
Mul(sav,++ran[n]);
up(i,1,n) {
++mid[p[i].fir][p[i].sec];
lr[p[i].fir].pb(p[i].sec);
rl[p[i].sec].pb(p[i].fir);
Add(f[p[i].fir][p[i].sec],mul[n-1]);
}
n<<=1;
up(r,1,n) {
dn(l,r,1) cnt[l][r]=cnt[l+1][r]+mid[l][r];
up(l,1,r) cnt[l][r]=cnt[l][r]+cnt[l][r-1];
}
up(len,2,n) {
up(l,1,n-len+1) {
int r=l+len-1;
if(mid[l][r]) Add(f[l][r],s[l+1][r-1]);
for(int u:lr[l]) {
if(u>=r) continue;
Add(f[l][r],(R[l+1][r]-R[u+1][r])%P);
}
for(int u:rl[r]) {
if(u<=l) continue;
Add(f[l][r],(L[l][r-1]-L[l][u-1])%P);
}
if(len<n) Mul(f[l][r],inv[n/2-cnt[l][r]]);
L[l][r]=(L[l][r-1]+f[l][r])%P;
R[l][r]=(R[l+1][r]+f[l][r])%P;
s[l][r]=(s[l+1][r]+L[l][r])%P;
}
}
Mul(Ans,f[1][n]);
up(i,1,n) up(j,1,n) f[i][j]=mid[i][j]=L[i][j]=R[i][j]=s[i][j]=0;
up(i,1,n) lr[i].clear(), rl[i].clear();
}
signed main() {
freopen("cup.in","r",stdin);
freopen("cup.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
int n; cin >> n;
vector<pii> a(n+1);
up(i,1,n) cin >> a[i].fir >> a[i].sec;
mul[0]=inv[0]=inv[1]=1;
up(i,1,n) mul[i]=(ll)mul[i-1]*i%P;
up(i,2,n) inv[i]=(ll)inv[P%i]*(P-P/i)%P;
sort(&a[1],&a[n+1]);
for(int l=1, r=1; l<=n; l=r+1, r=l) {
int lim=a[l].sec;
while(r<n&&a[r+1].fir<lim) lim=max(lim,a[++r].sec);
up(i,l,r) p[i-l+1]=mp(a[i].fir-a[l].fir+1,a[i].sec-a[l].fir+1);
solve(r-l+1);
}
cout << ((ll)Ans*sav%P+P)%P << '\n';
return 0;
}
朴素数据结构题(mex)
\(\text{mex}\) 一贯先转成从正整数,感觉会漂亮一些,然后 \(\text{mex}\geq u\) 的条件就是 \(u\leq \sum[a_i\leq u]\) 。
考虑单组怎么做,你要考虑二维平面不同高度的差分(这里指的是主席树/前缀和的差分,或者说这一段横坐标)的点数是否大于高度,这个东西混乱无序感觉根本没办法上技巧云云,只能暴力按照 \(ans\downarrow\) 去扫,动态记录有多少个 \(\leq ans\) 的数。
那我们考虑怎么一起做这个暴力,先想想怎么快速给包含 \(u\) 的区间计数减 \(1\)。只有相交是好做的,对排序后的一段区间有贡献,只有包含也是好做的,可以求出超区间的答案之后再加入该区间。这两个是很容易无机结合一下的,就是细节很多,给个大概的实现框架,讨论部分细节(
- 树状数组,用于加入区间的时候快速查询 \(\sum_{i=l}^r [a_i\leq ans]\)。
- 排序+双指针,用于扫询问和扫 \(u\)。
- 线段树,维护还未加入的区间,要做快速查找新的不被包含区间,发现可以在 \(r>i\) 的部分查找 \(l\) 最小的部分并判断是否 \(l<j\) 然后更新 \(i\),那么线段树二分一下,还要做动态加删。
- set,维护加入的区间,要快速查找一些需要的端点。
- 线段树,维护询问,存在的部分左右端点都是递增的但是因为前面按照 \(r\uparrow\) 那干脆一开始重构了编号用这个,维护区间加和查找全局最大值。
#pragma GCC optimize(1,2,3,"Ofast","inline")
#include<bits/stdc++.h>
#define ll long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define l first
#define r second
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
using namespace std;
const int N=500005, inf=1e9;
int n, T, a[N], pr[N], rp[N], Ans[N];
pii sa[N], sb[N], b[N];
struct BIT {
int tr[N];
void add(int x,int v) { for( ; x<=n; x+=x&-x) tr[x]+=v; }
int ask(int x) { int res=0; for( ; x; x-=x&-x) res+=tr[x]; return res; }
int ask(int l,int r) { return ask(r)-ask(l-1); }
} arr;
struct SUB {
int tr[N<<2], tag[N<<2];
inline void tup(int p) { tr[p]=max(tr[ls(p)],tr[rs(p)]); }
inline void tdn(int p) {
tr[ls(p)]+=tag[p], tag[ls(p)]+=tag[p];
tr[rs(p)]+=tag[p], tag[rs(p)]+=tag[p];
tag[p]=0;
}
void build() { up(i,1,4*T) tr[i]=-inf; }
void add(int x,int p=1,int s=1,int e=T) {
if(s==e) return tr[p]=arr.ask(b[s].l,b[s].r), void();
tdn(p);
int mid=(s+e)>>1;
x<=mid?add(x,ls(p),s,mid):add(x,rs(p),mid+1,e);
tup(p);
}
void del(int x,int p=1,int s=1,int e=T) {
if(s==e) return tr[p]=-inf, void();
tdn(p);
int mid=(s+e)>>1;
x<=mid?del(x,ls(p),s,mid):del(x,rs(p),mid+1,e);
tup(p);
}
void modify(int l,int r,int p=1,int s=1,int e=T) {
if(l<=s&&e<=r) return --tr[p], --tag[p], void();
tdn(p);
int mid=(s+e)>>1;
if(l<=mid) modify(l,r,ls(p),s,mid);
if(r>mid) modify(l,r,rs(p),mid+1,e);
tup(p);
}
int query(int lim,int p=1,int s=1,int e=T) {
if(tr[p]<lim) return 0;
if(s==e) return s;
tdn(p);
int mid=(s+e)>>1;
return tr[ls(p)]>=lim?query(lim,ls(p),s,mid):query(lim,rs(p),mid+1,e);
}
void check(int p=1,int s=1,int e=T) {
if(s==e) { cout << tr[p] << ' '; return; }
tdn(p);
int mid=(s+e)>>1;
check(ls(p),s,mid);
check(rs(p),mid+1,e);
}
} Q;
struct Grood {
int tl[N], tr[N];
multiset<int> L, R;
void build() { L.insert(0), L.insert(n+1), R.insert(0), R.insert(n+1); tl[n+1]=tr[n+1]=T+1; }
inline pii query(int x) { return mp(tr[*R.lower_bound(x)],tl[*--L.upper_bound(x)]); }
inline void add(int u) { int l=b[u].l, r=b[u].r; tl[l]=tr[r]=u, L.insert(l), R.insert(r); }
inline void del(int u) { int l=b[u].l, r=b[u].r; L.erase(L.find(l)), R.erase(R.find(r)); }
inline int pre(int u) { return *--R.lower_bound(b[u].r); }
inline int suf(int u) { return * L.lower_bound(b[u].l); }
} god;
struct Baaad {
int tr[N<<2];
inline void tup(int p) {
if(!tr[ls(p)]||!tr[rs(p)]) tr[p]=tr[ls(p)]|tr[rs(p)];
else tr[p]=b[tr[ls(p)]].l<b[tr[rs(p)]].l?tr[ls(p)]:tr[rs(p)];
}
void build(int p=1,int s=1,int e=T) {
if(s==e) return tr[p]=s, void();
int mid=(s+e)>>1;
build(ls(p),s,mid), build(rs(p),mid+1,e);
tup(p);
}
void modify(int x,int p=1,int s=1,int e=T) {
if(s==e) return tr[p]=0, void();
int mid=(s+e)>>1;
x<=mid?modify(x,ls(p),s,mid):modify(x,rs(p),mid+1,e);
tup(p);
}
int query(int low,int p=1,int s=1,int e=T) {
if(!tr[p]||low<=b[s].r) return tr[p];
int mid=(s+e)>>1, l=0, r=query(low,rs(p),mid+1,e);
if(low<=b[mid].r) l=query(low,ls(p),s,mid);
return (!l||!r)?(l|r):(b[l].l<b[r].l?l:r);
}
} bad;
void insert(int L,int R) {
int u;
while(u=bad.query(L+1)) {
if(b[u].l>=R) break;
Q.add(u), god.add(u), bad.modify(u), L=b[u].r;
}
}
signed main() {
freopen("mex.in","r",stdin);
freopen("mex.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> T;
up(i,1,n) cin >> a[i], sa[i]=mp(a[i]=min(a[i],n)+1,i);
up(i,1,T) pr[i]=i, cin >> sb[i].l >> sb[i].r;
sort(pr+1,pr+1+T,[](int i,int j){return sb[i].r<sb[j].r;});
up(i,1,T) b[i]=sb[pr[i]], rp[pr[i]]=i;
sort(sa+1,sa+1+n,[](pii i,pii j){return i.l>j.l;});
up(i,1,n) arr.add(i,1);
Q.build(), god.build(), bad.build();
insert(0,n+1);
int o=1;
dn(ans,n+1,0) {
int u;
while(u=Q.query(ans)) {
Ans[pr[u]]=ans;
god.del(u), Q.del(u);
insert(god.pre(u),god.suf(u));
}
while(o<=n&&sa[o].l==ans) {
int u=sa[o++].r;
arr.add(u,-1);
pii seg=god.query(u);
if(seg.l&&seg.r&&seg.l<=seg.r) Q.modify(seg.l,seg.r);
}
}
up(i,1,T) cout << Ans[i] << '\n';
return 0;
}

浙公网安备 33010602011771号