vp + 补题 + 随机做题 记录七
按随机顺序排序
The 2022 ICPC Asia Nanjing Regional Contest H Factories Once More
诶呦这个位移怎么这么唐(恼)。
一看到限制 \(k\) 个显然大概率凸性,一看树上显然启发式合并。
然后就是推转移了。
场上我是实事求是的转移,结果因为转移方程下标不独立死的很惨。
事实上,我们可以假定子树外面无论如何已经选好 \(k\) 个点了,这时候转移方程就下标独立了。
具体的,转移方程为:\(h_{i}=\max_{j\leq i}g_{i-j}+f_{x, j}+(K-j)\times j\times w[j]\)。
容易发现后面的东西是凸的,所以过程中所有函数都是凸的,可以直接闵和。
新增这个东西容易发现差分是等差数列,所以写一个平衡树下放等差数列加标记即可。
时间复杂度为 \(O(n\log n)\),偷懒写了 \(2\log\)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=1e5+5;
mt19937 rnd(100000193);
int n, m;
vector<pii> e[N];
int w[N];
struct node{
int ls, rs;
int siz, pri;
ll val;
ll tagfi, tagdt;
}tr[N*20];
int rt[N];
int idx;
void apply(int x, ll mfi, ll mdt){
tr[x].val+=mdt*tr[tr[x].ls].siz+mfi;
tr[x].tagfi+=mfi; tr[x].tagdt+=mdt;
}
void down(int rt){
if(tr[rt].rs) apply(tr[rt].rs, tr[rt].tagfi+tr[rt].tagdt*(tr[tr[rt].ls].siz+1), tr[rt].tagdt);
if(tr[rt].ls) apply(tr[rt].ls, tr[rt].tagfi, tr[rt].tagdt);
tr[rt].tagfi=0; tr[rt].tagdt=0;
}
void push_up(int rt){
tr[rt].siz=1+tr[tr[rt].ls].siz+tr[tr[rt].rs].siz;
return ;
}
int Merge(int x, int y){
if(x==0||y==0) return x+y;
down(x); down(y);
if(tr[x].pri<tr[y].pri){
tr[x].rs=Merge(tr[x].rs, y);
push_up(x);
return x;
}
tr[y].ls=Merge(x, tr[y].ls);
push_up(y);
return y;
}
void Split(int rt, ll v, int &x, int &y){
if(rt==0){
x=y=0;
return ;
}
down(rt);
if(tr[rt].val>=v){
x=rt;
Split(tr[rt].rs, v, tr[x].rs, y);
push_up(x);
}
else{
y=rt;
Split(tr[rt].ls, v, x, tr[y].ls);
push_up(y);
}
}
int add_node(int rt, ll v){
++idx;
tr[idx].siz=1; tr[idx].pri=rnd();
tr[idx].val=v;
int A=0, B=0;
Split(rt, v, A, B);
return Merge(Merge(A, idx), B);
}
void dfs2(int rt, int &crt){
if(rt==0) return ;
down(rt);
crt=add_node(crt, tr[rt].val);
dfs2(tr[rt].ls, crt);
dfs2(tr[rt].rs, crt);
}
void dfs(int x, int fa){
vector<int> son;
int mx=0;
for(auto edg:e[x]) {
int y=edg.fi;
if(y==fa) continue;
w[y]=edg.se;
dfs(y, x);
son.ep(y);
if(tr[rt[y]].siz>tr[rt[mx]].siz) mx=y;
apply(rt[y], (m-1)*w[y], -2*w[y]);
}
if(son.empty()){
rt[x]=++idx;
tr[idx].siz=1; tr[idx].pri=rnd();
return ;
}
rt[x]=rt[mx];
rt[x]=add_node(rt[x], 0ll);
for(auto y:son){
if(y==mx) continue;
dfs2(rt[y], rt[x]);
}
}
vector<ll> res;
void dfs3(int rt){
if(rt==0) return ;
down(rt);
res.ep(tr[rt].val);
dfs3(tr[rt].ls); dfs3(tr[rt].rs);
}
int main(){
// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
read(n); read(m);
for(int i=1, x, y, w; i<n; ++i){
read(x); read(y); read(w);
e[x].ep(y, w);
e[y].ep(x, w);
}
dfs(1, 0);
ll ans=0;
dfs3(rt[1]);
sort(res.begin(), res.end(), greater<ll>());
for(int i=0; i<m; ++i) ans+=res[i];
printf("%lld\n", ans);
return 0;
}
Moscow International Workshops 2020. Day 6. Grand Prix of Xiaomi D Rikka with New Year's Party
最开始我的转化是位置 \(i\) 的映射值 \(a_i\) 是左边最近的 \(s[i]\) 的位置,那么相当于每次拿出一个后缀,后缀 \(a[i, n]\) 的每一位都减去 \(i\),与 \(0\) 取 \(\max\)。答案就是把这些后缀的每个前缀拿在一起去重后的集合大小。
正确性显然,但每个后缀需要每一位减去 \(i\) 的操作使得我无法快速比较两个后缀的字典序,这个做法就寄了。
正解就是把映射值改为到左边最近的 \(s[i]\) 的距离,这时候就不需要整体做减法了,只需要特殊考虑一些位置会成为最靠前的位置(即变为 0)。
然而这样的位置只有 26 个,所以直接暴力即可。
用 SA 复杂度为 \(O(n\log^2 n+26n\log n)\),因为字符集较大建 SA 不能用基数排序(不过也许存在其他的方法用更低复杂度建这个 SA),用 Hash 加 \(\log\) 是不能通过的,很遗憾。
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=1e5+5;
int n, w;
char s[N];
int a[N], p[N];
vector<int> occ[26];
vector<int> ban[N];
int sa[N], rk[N], oldrk[N];
void get_sa(){
for(int i=1; i<=n; ++i) sa[i]=i, rk[i]=a[i];
for(w=1; w<n; w<<=1) {
sort(sa+1, sa+n+1, [&](int x, int y) {return rk[x]==rk[y]?rk[x+w]<rk[y+w]:rk[x]<rk[y];});
memcpy(oldrk, rk, sizeof(rk));
for (int p=0, i=1; i<=n; ++i) {
if (oldrk[sa[i]]==oldrk[sa[i-1]]&&oldrk[sa[i]+w]==oldrk[sa[i-1]+w]) {
rk[sa[i]]=p;
} else {
rk[sa[i]]=++p;
}
}
}
}
int ht[N];
void get_ht(){
for(int i=1; i<=n; ++i) rk[sa[i]]=i;
for(int i=1, k=0; i<=n; ++i){
if(rk[i]==1) continue;
if(k) k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&a[i+k]==a[j+k]) k++;
ht[rk[i]]=k;
}
}
int st[N][20], lg[N];
void build(){
for(int i=2; i<=n; ++i) lg[i]=lg[i>>1]+1;
for(int i=1; i<=n; ++i) st[i][0]=ht[i];
for(int t=1; t<=lg[n]; ++t){
for(int i=1; i+(1<<t)-1<=n; ++i){
st[i][t]=min(st[i][t-1], st[i+(1<<(t-1))][t-1]);
}
}
}
inline int lcp(int x, int y){
if(x==y) return n-x+1;
x=rk[x]; y=rk[y];
if(x>y) swap(x, y);
++x;
int t=lg[y-x+1];
return min(st[x][t], st[y-(1<<t)+1][t]);
}
inline int getv(int l, int len){
for(auto t:ban[l]) if(t==len) {
return 0;
}
return a[l+len-1];
}
inline int lcpp(int x, int y){
if(x>y) swap(x, y);
vector<pii> tem;
int it1=0, it2=0;
while(it1<(int)ban[x].size()||it2<(int)ban[y].size()){
if(it1==(int)ban[x].size()){
if(!tem.empty()&&tem.back().fi==ban[y][it2]){
tem.back().se+=2;
}
else tem.ep(ban[y][it2], 2);
++it2;
}
else if(it2==(int)ban[y].size()){
if(!tem.empty()&&tem.back().fi==ban[x][it1]){
tem.back().se+=1;
}
else tem.ep(ban[x][it1], 1);
++it1;
}
else {
if(ban[x][it1]<=ban[y][it2]){
if(!tem.empty()&&tem.back().fi==ban[x][it1]){
tem.back().se+=1;
}
else tem.ep(ban[x][it1], 1);
++it1;
}
else{
if(!tem.empty()&&tem.back().fi==ban[y][it2]){
tem.back().se+=2;
}
else tem.ep(ban[y][it2], 2);
++it2;
}
}
}
while(!tem.empty()&&tem.back().fi>n-y+1) tem.pop_back();
int ret=0;
for(auto t:tem){
int w=lcp(x+ret, y+ret);
if(w<t.fi-ret-1){
ret+=w;
return ret;
}
ret=t.fi-1;
if(t.se==3){
++ret;
}
else if(t.se==2&&a[x+ret]==0){
++ret;
}
else if(t.se==1&&a[y+ret]==0){
++ret;
}
else{
return ret;
}
}
if(ret!=n-y+1) ret+=lcp(x+ret, y+ret);
return ret;
}
inline bool cmp(int x, int y){
int t=lcpp(x, y);
if(t==n-max(x, y)+1){
return x>y;
}
else{
return getv(x, t+1)<getv(y, t+1);
}
}
int main(){
// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
read(n);
scanf("%s", s+1);
for(int i=1; i<=n; ++i){
if(occ[s[i]-'a'].empty()) a[i]=0;
else a[i]=i-occ[s[i]-'a'].back();
occ[s[i]-'a'].ep(i);
p[i]=i;
}
for(int i=1; i<=n; ++i){
for(int j=0; j<26; ++j){
if(lower_bound(occ[j].begin(), occ[j].end(), i)!=occ[j].end()){
ban[i].ep((*lower_bound(occ[j].begin(), occ[j].end(), i))-i+1);
}
}
sort(ban[i].begin(), ban[i].end());
}
get_sa();
get_ht();
build();
sort(p+1, p+n+1, cmp);
// for(int i=1; i<=n; ++i){
// cout<<p[i]<<endl;
// }
ll ans=n-p[1]+1;
for(int i=2; i<=n; ++i){
// cout<<p[i-1]<<' '<<p[i]<<' '<<lcpp(p[i-1], p[i])<<endl;
ans+=n-p[i]+1-lcpp(p[i-1], p[i]);
}
printf("%lld\n", ans);
return 0;
}
Moscow International Workshops 2020. Day 6. Grand Prix of Xiaomi B Rikka with Maximum Subsegment Sum
我最开始想的是类似历史版本和一样搭矩阵,但是很遗憾最大子段和需要 \((\max, +)\) 运算,这个实在是不好统计历史版本和。
后来想到 CDQ 分治,处理完子问题后,我们只需要计算跨过 \(mid, mid+1\) 的区间的答案。
记 \(u[i]\) 表示 \(i\) 到 \(mid\) 的最大子段和,\(v[i]\) 表示 \(mid+1\) 到 \(i\) 的最大子段和,\(p[i]\) 表示 \(i\) 到 \(mid\) 的最大后缀和,\(q[i]\) 表示 \(mid+1\) 到 \(i\) 的最大前缀和,对于区间 \([l, r]\),答案即为 \(\max\{ u[l], v[r], p[l]+q[r]\}\)。
枚举最大值,容易发现剩下的构成三个二位数点问题,直接做即可。
复杂度为 \(O(n\log n\log V)\),如果愿意离散化的话可以做到 \(O(n\log^2 n)\)。
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=1e5+5;
const ll inf=1e14;
int n; ll a[N];
ll sum[N];
ull ans=0;
ll u[N], v[N], p[N], q[N], tem[N];
inline bool cmp1(int x, int y){
return u[x]<u[y];
}
inline bool cmp2(int x, int y){
return v[x]<v[y];
}
inline bool cmp3(int x, int y){
return u[x]>u[y];
}
inline bool cmp4(int x, int y){
return v[x]>v[y];
}
inline bool cmp5(int x, int y){
return u[x]-p[x]>u[y]-p[y];
}
inline bool cmp6(int x, int y){
return q[x]>q[y];
}
ll tr[N*60]; int ls[N*60], rs[N*60];
int rt, idx;
inline void mdf(int &p, ll l, ll r, ll x, ll v){
if(!p) {
p=++idx;
tr[p]=ls[p]=rs[p]=0;
}
tr[p]+=v;
if(l==r) return ;
ll mid=(l+r)>>1;
if(x<=mid) mdf(ls[p], l, mid, x, v);
else mdf(rs[p], mid+1, r, x, v);
}
inline ll get(int p, ll l, ll r, ll L, ll R){
if(L>R) return 0;
if(!p) return 0;
if(L<=l&&r<=R) return tr[p];
ll mid=(l+r)>>1; ll ret=0;
if(L<=mid) ret=get(ls[p], l, mid, L, R);
if(R>mid) ret+=get(rs[p], mid+1, r, L, R);
return ret;
}
void solve(int l, int r){
if(l==r){
u[l]=v[l]=a[l];
ans+=a[l];
return ;
}
int mid=(l+r)>>1;
solve(l, mid); solve(mid+1, r);
p[mid]=a[mid];
for(int i=mid-1; i>=l; --i) p[i]=max(p[i+1], sum[mid]-sum[i-1]);
q[mid+1]=a[mid+1];
for(int i=mid+2; i<=r; ++i) q[i]=max(q[i-1], sum[i]-sum[mid]);
vector<int> v1, v2;
for(int i=l; i<=mid; ++i) v1.ep(i);
for(int i=mid+1; i<=r; ++i) v2.ep(i);
sort(v1.begin(), v1.end(), cmp1);
sort(v2.begin(), v2.end(), cmp2);
int it=0;
rt=idx=0;
for(auto t:v1){
while(it<(int)v2.size()&&v[v2[it]]<u[t]) mdf(rt, -inf, inf, q[v2[it]], 1), ++it;
ans+=1ull*u[t]*get(rt, -inf, inf, -inf, u[t]-p[t]-1);
}
sort(v1.begin(), v1.end(), cmp3);
sort(v2.begin(), v2.end(), cmp4);
it=0;
rt=idx=0;
for(auto t:v1){
while(it<(int)v2.size()&&v[v2[it]]>=u[t]) mdf(rt, -inf, inf, v[v2[it]]-q[v2[it]], v[v2[it]]), ++it;
ans+=get(rt, -inf, inf, p[t]+1, inf);
}
sort(v1.begin(), v1.end(), cmp5);
sort(v2.begin(), v2.end(), cmp6);
it=0;
rt=idx=0;
for(auto t:v1){
while(it<(int)v2.size()&&q[v2[it]]>=u[t]-p[t]) mdf(rt, -inf, inf, v[v2[it]]-q[v2[it]], 1), ++it;
ans+=1ull*p[t]*get(rt, -inf, inf, -inf, p[t]);
}
it=0;
rt=idx=0;
for(auto t:v1){
while(it<(int)v2.size()&&q[v2[it]]>=u[t]-p[t]) mdf(rt, -inf, inf, v[v2[it]]-q[v2[it]], q[v2[it]]), ++it;
ans+=get(rt, -inf, inf, -inf, p[t]);
}
tem[r]=u[r]=a[r];
for(int i=r-1; i>=l; --i) tem[i]=max(tem[i+1]+a[i], a[i]), u[i]=max(tem[i], u[i+1]);
tem[l]=v[l]=a[l];
for(int i=l+1; i<=r; ++i) tem[i]=max(tem[i-1]+a[i], a[i]), v[i]=max(tem[i], v[i-1]);
}
int main(){
// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
read(n);
for(int i=1; i<=n; ++i) read(a[i]), sum[i]=sum[i-1]+a[i];
solve(1, n);
printf("%llu\n", ans);
return 0;
}
Moscow Pre-Finals Workshop 2020. Day 2. GP of Tokyo L Yosupo's Algorithm
第一种思路考虑按 \(y\) 分治,这时候会发现分治区间任意一对左红右蓝都是答案备选项,看上去没有什么性质能把他们快速维护出来。
第二种思路考虑把询问和点放一起按 \(bx\) 分治,这时候会发现分治区间任意一对左询问右蓝点都是对红点的矩形约束,也不好快速维护。
考虑分析性质,按第一种分治,并非每个对都有用。事实上只有包含左红点最大值或右蓝点最大值的点对才会成为答案备选项。
证明假设最大红蓝点分别是 \(p, q\),考虑他们和某个询问 \(L, R\) 的关系:
-
\(L<x_p, x_q<R\),符合性质。
-
\(L<x_p\) 或 \(x_q<R\),符合性质。
-
\(L>x_p, x_q>R\),这个情况会在翻转坐标后考虑到。
或者还有一种证明,考虑某个点对 \((p,i)\) 和 \(L, R\) 的关系:
-
如果 \(p<L\),我们希望找到 \((j, q)\) 使得 \(j>L, x_q<R\)。
-
如果找不到,说明 \(x_q>R\),这个情况会在翻转坐标后考虑到。
于是每个分治区间只有区间长度个点对有用,或者换句话说我们给每个点对找到了一个支配点对,支配点对的数量级为 \(O(n\log n)\)。
查询直接数点即可。
复杂度 \(O(n\log^2 n+m\log n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=1e6+5;
int n, m;
int rx[N], ry[N], rw[N];
int bx[N], by[N], bw[N];
int ql[N], qr[N], ans[N];
void work(){
vector<int> vec;
for(int i=1; i<=n; ++i) vec.ep(rx[i]);
sort(vec.begin(), vec.end());
for(int i=1; i<=n; ++i) rx[i]=lower_bound(vec.begin(), vec.end(), rx[i])-vec.begin()+1;
for(int i=1; i<=m; ++i) ql[i]=lower_bound(vec.begin(), vec.end(), ql[i])-vec.begin();
vec.clear();
for(int i=1; i<=n; ++i) vec.ep(bx[i]);
sort(vec.begin(), vec.end());
for(int i=1; i<=n; ++i) bx[i]=lower_bound(vec.begin(), vec.end(), bx[i])-vec.begin()+1;
for(int i=1; i<=m; ++i) qr[i]=lower_bound(vec.begin(), vec.end(), qr[i])-vec.begin();
vec.clear();
for(int i=1; i<=n; ++i) vec.ep(ry[i]), vec.ep(by[i]);
sort(vec.begin(), vec.end());
for(int i=1; i<=n; ++i) {
ry[i]=lower_bound(vec.begin(), vec.end(), ry[i])-vec.begin()+1;
by[i]=lower_bound(vec.begin(), vec.end(), by[i])-vec.begin()+1;
}
}
int p[N];
inline bool cmp(int x, int y){
return (x<=n?ry[x]:by[x-n])<(y<=n?ry[y]:by[y-n]);
}
struct node{
int x, y, w;
};
vector<node> bin;
void solve(int l, int r){
if(l==r) return ;
int mid=(l+r)>>1;
int mxr=0, mxb=0;
for(int i=l; i<=mid; ++i){
if(p[i]<=n&&rw[p[i]]>rw[mxr]) mxr=p[i];
}
for(int i=mid+1; i<=r; ++i){
if(p[i]>n&&bw[p[i]-n]>bw[mxb]) mxb=p[i]-n;
}
if(mxr!=0){
for(int i=mid+1; i<=r; ++i){
if(p[i]<=n) continue;
bin.push_back((node){rx[mxr], bx[p[i]-n], rw[mxr]+bw[p[i]-n]});
}
}
if(mxb!=0){
for(int i=l; i<=mid; ++i){
if(p[i]>n) continue;
bin.push_back((node){rx[p[i]], bx[mxb], rw[p[i]]+bw[mxb]});
}
}
solve(l, mid); solve(mid+1, r);
}
int tr[N];
void add(int x, int w){
for(; x<=n; x+=(x&-x)) tr[x]=max(tr[x], w);
}
int get(int x){
int ret=-1;
for(; x>0; x-=(x&-x)) ret=max(ret, tr[x]);
return ret;
}
vector<pii> pt[N];
vector<int> ask[N];
void calc(){
memset(tr, -1, sizeof tr);
for(int i=1; i<=n+1; ++i) pt[i].clear(), ask[i].clear();
for(auto t:bin) pt[t.y].ep(t.x, t.w);
for(int i=1; i<=m; ++i) if(qr[i]>0) ask[qr[i]].ep(i);
for(int i=1; i<=n+1; ++i){
for(auto t:pt[i]) add(t.fi, t.se);
for(auto t:ask[i]) ans[t]=max(ans[t], get(ql[t]));
}
}
int main(){
// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
read(n);
for(int i=1; i<=n; ++i){
read(rx[i]); read(ry[i]); read(rw[i]);
rx[i]=-rx[i];
p[i]=i;
}
for(int i=1; i<=n; ++i){
read(bx[i]); read(by[i]); read(bw[i]);
p[i+n]=i+n;
}
read(m);
for(int i=1; i<=m; ++i){
read(ql[i]); read(qr[i]);
ql[i]=-ql[i];
ans[i]=-1;
}
work();
sort(p+1, p+2*n+1, cmp);
solve(1, 2*n);
calc();
for(auto &t:bin) t.x=n+1-t.x, t.y=n+1-t.y;
for(int i=1; i<=m; ++i) ql[i]=n-ql[i], qr[i]=n-qr[i];
calc();
for(int i=1; i<=m; ++i) printf("%d\n", ans[i]);
return 0;
}
The 2022 ICPC Asia Hangzhou Regional Contest E Oscar is All You Need
场上口胡了一个增量构造,没时间写了,赛后完善一下过了。
先大胆猜测除了 \(n=3\) 一定能把序列变成 \(a[i]=i\)。
假设我们已经把前 \(i\) 个排成了 \(a[i]=i\),记作段 \(A\)。即 \(i+1\) 的位置为 \(B\),分类讨论。
AXBZ
AXBZ
ZXBA
ABZX
AXYB
AXYB
YBXA
ABXY
AXB
我场上没想到怎么构造,太深邃了。
赛后通过搜索得到一组解:
perform(1, 1);
perform(1, 2);
perform(1, 1);
perform(2, 1);
perform(1, 1);
前两类会用两次操作变为更小的子问题,最后一类用 5 次操作完成整个序列的排序,不会超过 \(2n+1\) 的操作次数上限。
存在构造到上界的数据。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=1e6+5;
int T, n, m;
int a[N], b[N];
vector<pii> vec;
void perform(int x, int y){
vec.ep(x, y);
int it=0;
for(int i=n-y+1; i<=n; ++i) b[++it]=a[i];
for(int i=x+1; i<=n-y; ++i) b[++it]=a[i];
for(int i=1; i<=x; ++i) b[++it]=a[i];
for(int i=1; i<=n; ++i) a[i]=b[i];
}
void solve(){
read(n);
for(int i=1; i<=n; ++i) read(a[i]);
if(n==3){
if(a[1]>a[3]){
printf("1\n1 1\n");
}
else{
printf("0\n");
}
return ;
}
vec.clear();
if(a[1]!=1){
int pos=2;
for(int i=3; i<=n; ++i) if(a[i]==1) pos=i;
if(pos==2){
perform(2, 1);
perform(2, 1);
}
else{
perform(1, n-pos+1);
}
}
while(true){
int c=1;
while(c<n&&a[c+1]==c+1) ++c;
if(c==n) break;
if(c==n-2){
perform(1, 1);
perform(1, 2);
perform(1, 1);
perform(2, 1);
perform(1, 1);
break;
}
int pos=c+2;
for(int i=c+3; i<=n; ++i) if(a[i]==c+1) pos=i;
if(pos==n){
perform(c, 2);
perform(1, c);
}
else{
perform(c, n-pos);
perform(pos-1-c+n-pos, c);
}
}
// printf("seq:"); for(int i=1; i<=n; ++i) cout<<a[i]<<' '; cout<<endl;
printf("%d\n", (int)vec.size());
for(auto t:vec) printf("%d %d\n", t.fi, t.se);
}
int main(){
freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
read(T);
while(T--){
solve();
}
return 0;
}
The 2022 ICPC Asia Hangzhou Regional Contest L Levenshtein Distance
编辑距离没有简单的表达式,唯一有的只有 DP 式。要求的编辑距离范围较小,只有 30,所以考虑 DP 套 DP,虽然不完全是。
容易发现符合要求的 \(T\) 中的串和 \(S\) 的长度差不能超过 \(k\),所以把长度差压入状态。
依次考虑 \(T\) 的每个后缀,记 \(f_{i,j}\) 表示最大的长度 \(x\) 使得 \(s[1, x]\) 和 \(T^{\prime}[1, x+j]\) 的编辑距离不超过 \(i\)。
DP 转移式是很简单的,类比编辑距离的 DP 式就能写出,可以用 \(O(k^2)\) 的时间求出,考虑怎么求答案。
我们求的是最远距离,所以只需要考虑往回收缩,但状态数是 \(O(k^2)\) 的,如果暴力收缩复杂度为 \(O(k^3)\)。
回过头来,符合要求的 \(T\) 中的串和 \(S\) 的长度差不能超过 \(k\),所以只有 \(O(k)\) 个位置会被更新到,只需要记录这些位置的 dp 值,从后往前递推一下即可。
复杂度为 \(O(nk^2+n\log n)\),需要用后缀数组求 \(lcp\)。
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=2e5+5;
int B, n, m;
char s[N], t[N];
int x[N],y[N],c[N],sa[N],ht[N],rk[N];
void get_sa(){
for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
int m='z';
for(int i=2;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i;i--) sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num=0;
for(int i=n-k+1;i<=n;i++) y[++num]=i;
for(int i=1;i<=n;i++){
if(sa[i]>k) y[++num]=sa[i]-k;
}
for(int i=1;i<=m;i++) c[i]=0;
for(int i=1;i<=n;i++) c[x[i]]++;
for(int i=2;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
x[sa[1]]=1;num=1;
for(int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(num==n) break;
m=num;
}
}
void get_ht(){
for(int i=1; i<=n; ++i) rk[sa[i]]=i;
for(int i=1, k=0; i<=n; ++i){
if(rk[i]==1) continue;
if(k) k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
ht[rk[i]]=k;
}
}
int st[N][20], lg[N];
void build(){
for(int i=2; i<=n; ++i) lg[i]=lg[i>>1]+1;
for(int i=1; i<=n; ++i) st[i][0]=ht[i];
for(int t=1; t<=lg[n]; ++t){
for(int i=1; i+(1<<t)-1<=n; ++i){
st[i][t]=min(st[i][t-1], st[i+(1<<(t-1))][t-1]);
}
}
}
inline int lcp(int x, int y){
if(x>n||y>n+m+1) return 0;
x=rk[x]; y=rk[y];
if(x>y) swap(x, y);
++x;
int t=lg[y-x+1];
return min(st[x][t], st[y-(1<<t)+1][t]);
}
int f[35][65];
int eps=32;
int ans[35];
int dp[N];
int main(){
// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
read(B);
scanf("%s", s+1); n=strlen(s+1);
scanf("%s", t+1); m=strlen(t+1);
s[n+1]='&';
for(int i=1; i<=m; ++i) s[n+1+i]=t[i];
n+=m+1;
get_sa();
get_ht();
build();
n-=m+1;
for(int st=1; st<=m; ++st){
memset(f, -0x3f, sizeof f);
f[0][eps]=0;
for(int i=0; i<=B; ++i){
for(int j=-B; j<=B; ++j) if(f[i][j+eps]>=0){
f[i][j+eps]=min(f[i][j+eps], n);
f[i][j+eps]+=lcp(f[i][j+eps]+1, n+1+st+f[i][j+eps]+j);
f[i+1][j+eps]=max(f[i+1][j+eps], f[i][j+eps]+1);
f[i+1][j+1+eps]=max(f[i+1][j+1+eps], f[i][j+eps]);
f[i+1][j-1+eps]=max(f[i+1][j-1+eps], f[i][j+eps]+1);
}
}
for(int i=max(st, st+n-B-1); i-1<=st+n+B-1&&i-1<=m; ++i) dp[i]=B+1;
for(int i=0; i<=B; ++i){
for(int j=-B; j<=B; ++j) if(f[i][j+eps]==n){
int cur=st+n+j-1;
if(cur<st||cur>m) continue;
dp[cur]=min(dp[cur], i);
}
}
for(int i=min(st+n+B-1, m); i>=st+n-B-1&&i>=st; --i) {
dp[i]=min(dp[i], dp[i+1]+1);
++ans[dp[i]];
}
// for(int i=0; i<=B; ++i) printf("%lld ", ans[i]); putchar('\n');
}
for(int i=0; i<=B; ++i) printf("%d\n", ans[i]);
return 0;
}
The 2022 ICPC Asia Hangzhou Regional Contest B Useful Algorithm
useful 在哪。
水题。
逐位考虑,容易发现当且仅当 \(c_x+c_y\geq 2^{i}\) 时会产生进位,拆下标变为 \(g_{x}\geq f_{y}\),其中 \(g_{x}=x, f_{x}=2^{i}-x\)。
直接线段树即可。
复杂度为 \(O(nm^2+nm\log n+qm^2+qm\log n)\),需要一个可删堆。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=1e6+5, M=(1<<17)+5;
const ll inf=1e15;
int n, m, q, bd;
ll w[17];
int c[N]; ll d[N];
ll ans;
struct myheap{
priority_queue<ll> p, q;
void push(ll x){
p.push(x);
}
void pop(ll x){
q.push(x);
}
ll top(){
while(!q.empty()&&p.top()==q.top()){
p.pop(); q.pop();
}
return p.top();
}
};
struct DS{
ll g[M<<2], f[M<<2], res[M<<2];
void up(int p){
res[p]=max(max(res[p<<1], res[p<<1|1]), f[p<<1]+g[p<<1|1]);
f[p]=max(f[p<<1], f[p<<1|1]);
g[p]=max(g[p<<1], g[p<<1|1]);
}
void mdff(int p, int l, int r, int x, ll v){
if(l==r){
f[p]=v; res[p]=f[p]+g[p];
return ;
}
int mid=(l+r)>>1;
if(x<=mid) mdff(p<<1, l, mid, x, v);
else mdff(p<<1|1, mid+1, r, x, v);
up(p);
}
void mdfg(int p, int l, int r, int x, ll v){
if(l==r){
g[p]=v; res[p]=f[p]+g[p];
return ;
}
int mid=(l+r)>>1;
if(x<=mid) mdfg(p<<1, l, mid, x, v);
else mdfg(p<<1|1, mid+1, r, x, v);
up(p);
}
void build(int p, int l, int r){
g[p]=f[p]=res[p]=-inf;
if(l==r){
return ;
}
int mid=(l+r)>>1;
build(p<<1, l, mid); build(p<<1|1, mid+1, r);
}
myheap mxd[M], mxd2[M];
void add(int x, int y, ll v){
if(x==0) return ;
mxd[x].push(v);
mdfg(1, 0, bd, x, mxd[x].top());
mxd2[y].push(v);
mdff(1, 0, bd, y, mxd2[y].top());
}
void del(int x, int y, ll v){
if(x==0) return ;
mxd[x].pop(v);
mdfg(1, 0, bd, x, mxd[x].top());
mxd2[y].pop(v);
mdff(1, 0, bd, y, mxd2[y].top());
}
}t[17];
void calc(){
ans=0;
for(int i=1; i<=m; ++i) {
if(t[i].res[1]<0) continue;
ans=max(ans, w[i]*t[i].res[1]);
}
printf("%lld\n", ans);
}
int main(){
// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
read(n); read(m); read(q); bd=(1<<m)-1;
for(int i=1; i<=m; ++i) {
read(w[i]), t[i].build(1, 0, bd);
for(int j=0; j<(1<<i); ++j) t[i].mxd[j].push(-inf), t[i].mxd2[j].push(-inf);
}
for(int i=1; i<=n; ++i) read(c[i]);
for(int i=1; i<=n; ++i) read(d[i]);
for(int i=1; i<=n; ++i){
for(int j=1; j<=m; ++j){
int cc=c[i]&((1<<j)-1);
t[j].add(cc, (1<<j)-cc, d[i]);
}
}
calc();
while(q--){
ull x, u, v;
read(x); read(u); read(v);
x^=ans; u^=ans; v^=ans;
for(int j=1; j<=m; ++j){
int cc=c[x]&((1<<j)-1);
t[j].del(cc, (1<<j)-cc, d[x]);
}
c[x]=u; d[x]=v;
for(int j=1; j<=m; ++j){
int cc=c[x]&((1<<j)-1);
t[j].add(cc, (1<<j)-cc, d[x]);
}
calc();
}
return 0;
}
The 2022 ICPC Asia Hangzhou Regional Contest J Painting
妙题。
对于一条线段,我们把拦截它的线段和它相连,那么最后会形成一棵树。
对于树上一条路径,会形成一个凸壳。
加入某条线段,我们找到起点距离它最近的两侧的线段(人为加上下边界),会拦截它的线段只存在与这条路径上。
利用二分即可求出具体和谁相交,不过这里用倍增更方便。
复杂度写的比较丑,是 \(O(n\log n\log V)\) 的,可以做的更好。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=1e6+5;
int T, n, W, m;
int f[N][20], dep[N];
__int128 abs(__int128 x){
if(x<0) x=-x;
return x;
}
struct frac{
__int128 x, y;
void refresh(){
if(y<0) y=-y, x=-x;
__int128 d=__gcd(abs(x), y);
x/=d; y/=d;
}
};
frac operator +(frac a, frac b){
a.x=a.x*b.y+a.y*b.x;
a.y=a.y*b.y;
a.refresh();
return a;
}
frac operator -(frac a, frac b){
a.x=a.x*b.y-a.y*b.x;
a.y=a.y*b.y;
a.refresh();
return a;
}
frac operator *(frac a, frac b){
a.x=a.x*b.x;
a.y=a.y*b.y;
a.refresh();
return a;
}
frac operator /(frac a, frac b){
a.x=a.x*b.y;
a.y=a.y*b.x;
a.refresh();
return a;
}
bool operator <(frac a, frac b){
return (__int128)a.x*b.y<(__int128)a.y*b.x;
}
bool operator ==(frac a, frac b){
return (__int128)a.x*b.y==(__int128)a.y*b.x;
}
frac edx[N], edy[N], stx[N], sty[N];
pair<frac, frac> intersect(frac a, frac x, frac y, frac aa, frac xx, frac yy){
//(y-a) x1/x+a=y1 | (yy-aa) x2/xx+aa=y2
//x=xx*x*(aa-a)/[xx*(y-a)-x*(yy-aa)]
//y=xx*x*(aa-a)/[xx*(y-a)-x*(yy-aa)]*xx*(y-a)+a*xx*x
frac bs=(y-a)/x-(yy-aa)/xx;
if(bs==(frac){0, 1}){
return mapa((frac){-1, -1}, (frac){-1, -1});
}
frac nx=(aa-a)/bs;
if(nx<(frac){0, 1}||xx<nx){
return mapa((frac){-1, -1}, (frac){-1, -1});
}
frac ny=(y-a)*nx/x+a;
return mapa(nx, ny);
}
int lca(int x, int y){
if(dep[x]<dep[y]) swap(x, y);
for(int i=19; i>=0; --i) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=19; i>=0; --i) if(f[x][i]^f[y][i]) x=f[x][i], y=f[y][i];
return f[x][0];
}
int main(){
// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
read(n); read(W);
dep[1]=1;
edx[2]=(frac){W, 1};
edy[2]=(frac){0, 1};
stx[2]=(frac){0, 1};
sty[2]=(frac){0, 1};
edx[3]=(frac){W, 1};
edy[3]=(frac){1000001, 1};
stx[3]=(frac){0, 1};
sty[3]=(frac){1000001, 1};
f[2][0]=1; f[3][0]=1; dep[2]=2; dep[3]=2;
m=3;
set<pii> st;
st.insert(mapa(0, 2)); st.insert(mapa(1000001, 3));
for(int i=1, a, b, c; i<=n; ++i){
read(a); read(b); read(c);
set<pii>::iterator it=st.upper_bound(mapa(a, 0));
int v=(*it).se;
--it;
int u=(*it).se;
int w=lca(u, v);
int ret=w;
int _u=u, _v=v;
for(int i=19; i>=0; --i){
if(dep[f[u][i]]<=dep[w]) continue;
pair<frac, frac> ask=intersect((frac){a, 1}, (frac){W, 1}, (frac){b, 1}, sty[_u], edx[f[u][i]], edy[f[u][i]]);
if(ask.fi.x==-1){
u=f[u][i];
}
else{
ret=f[u][i];
}
}
{
pair<frac, frac> ask=intersect((frac){a, 1}, (frac){W, 1}, (frac){b, 1}, sty[_u], edx[u], edy[u]);
if(ask.fi.x!=-1){
ret=u;
}
}
if(ret==w){
for(int i=19; i>=0; --i){
if(dep[f[v][i]]<=dep[w]) continue;
pair<frac, frac> ask=intersect((frac){a, 1}, (frac){W, 1}, (frac){b, 1}, sty[_v], edx[f[v][i]], edy[f[v][i]]);
if(ask.fi.x==-1){
v=f[v][i];
}
else{
ret=f[v][i];
}
}
{
pair<frac, frac> ask=intersect((frac){a, 1}, (frac){W, 1}, (frac){b, 1}, sty[_v], edx[v], edy[v]);
if(ask.fi.x!=-1){
ret=v;
}
}
}
frac cx, cy;
if(ret==1){
cx=(frac){W, 1}; cy=(frac){b, 1};
}
else{
pair<frac, frac> ask=intersect((frac){a, 1}, (frac){W, 1}, (frac){b, 1}, sty[ret], edx[ret], edy[ret]);
cx=ask.fi, cy=ask.se;
}
printf("(%lld/%lld,%lld/%lld)\n", (ll)cx.x, (ll)cx.y, (ll)cy.x, (ll)cy.y);
if(c==0) continue;
++m;
stx[m]=(frac){0, 1};
sty[m]=(frac){a, 1};
edx[m]=cx; edy[m]=cy;
f[m][0]=ret; dep[m]=dep[ret]+1;
for(int i=1; i<20; ++i) f[m][i]=f[f[m][i-1]][i-1];
st.insert(mapa(a, m));
}
return 0;
}
2025“钉耙编程”中国大学生算法设计暑期联赛(1)1008 mod 2
很遗憾,手速被检测为不通过。
先考虑答案是什么,显然是一堆组合数乘起来,答案对 2 取模启发我们用卢卡斯,等价于划分 \(k\) 的二进制位给每个权值作为出现次数,容易发现任意两个权值出现次数不能相同,因此相当于定序。
我们可以额外加出现奇数次的权值,但显然只能有奇数种,进一步分析可以得到只能有一种。
\(k\) 最多 59 位,所以当 \(|odd(a[L, R])|\) 大于等于 60 时答案为 0。
再按 \(k\) 奇偶性讨论,如果 \(k\) 为偶数,则不能额外加权值,直接做 dp 算方案数。
如果 \(k\) 为奇数,先预留一个 \(1\),剩下的转化为先把 \(k\) 的某些位选走,再做上面的 dp。
对比系数:\(C_{k}^{1}\times C_{\frac{k-1}{2}}^{t}\) 和 \(C_{k}^{2t+1}\),发现奇偶性相同,所以直接把 dp 值加起来即可。
求 \(odd(a[L, R])\) 可以用异或哈希在主席树上暴力。
复杂度为 \(O(n\log n+q\log n\log k+\log^4k)\)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=1e5+5;
int T, n, m;
int a[N], b[N];
mt19937_64 rnd(time(0));
ull hs[N];
ull tr[N*100], tag[N*100]; int ls[N*100], rs[N*100];
int idx, rt[N];
inline int gen(){
++idx;
tr[idx]=0; ls[idx]=0; rs[idx]=0;
return idx;
}
void mdf(int &p, int q, int l, int r, int x){
p=gen();
tr[p]=tr[q]; ls[p]=ls[q]; rs[p]=rs[q];
tr[p]^=hs[x];
if(l==r){
return ;
}
int mid=(l+r)>>1;
if(x<=mid) mdf(ls[p], ls[q], l, mid, x);
else mdf(rs[p], rs[q], mid+1, r, x);
}
vector<int> vec;
void pick(int p, int q, int l, int r){
if(vec.size()==60) return ;
if((tr[p]^tr[q])==0) return ;
if(l==r){
vec.push_back(l);
return ;
}
int mid=(l+r)>>1;
pick(ls[p], ls[q], l, mid); pick(rs[p], rs[q], mid+1, r);
}
int dp[61][61][61];
void init(){
dp[0][0][0]=1;
for(int i=0; i<60; ++i){
for(int j=0; j<60; ++j){
for(int k=0; k<60; ++k) if(dp[i][j][k]){
dp[i+1][j][k+1]^=1;
for(int w=0; w<=k; ++w) if((w&k)==w) dp[i+1][j+1][k-w]^=1;
}
}
}
}
void solve(){
read(n);
for(int i=1; i<=n; ++i) read(a[i]), b[i]=a[i];
sort(b+1, b+n+1);
int len=unique(b+1, b+n+1)-b-1;
for(int i=1; i<=n; ++i) a[i]=lower_bound(b+1, b+len+1, a[i])-b;
for(int i=1; i<=n; ++i) hs[i]=rnd();
idx=0;
for(int i=1; i<=n; ++i) mdf(rt[i], rt[i-1], 1, n, a[i]);
read(m);
int preans=0;
while(m--){
int l, r; ll k, v;
read(l); read(r); read(k); read(v);
l^=preans; r^=preans; k^=preans; v^=preans;
vec.clear();
pick(rt[r], rt[l-1], 1, n);
if(vec.size()==60){
printf("0\n");
continue;
}
sort(vec.begin(), vec.end());
if(!vec.empty()&&b[vec.back()]>v){
printf("0\n");
continue;
}
int ans=0;
if(k&1){
if((v-vec.size())&1){
int t=__builtin_popcountll((k-1)/2);
for(int i=0; i<=t; ++i){
if((i&t)!=i) continue;
ans^=dp[t-i][vec.size()][0];
}
}
}
else{
ans=dp[__builtin_popcountll(k/2)][vec.size()][0];
}
printf("%d\n", ans);
preans+=ans;
}
}
int main(){
// freopen("D:\\nya\\acm\\B\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\B\\test.out","w",stdout);
init();
read(T);
while(T--){
solve();
}
return 0;
}
- upd by Appleblue17
这不就是斯特林吗?
2025“钉耙编程”中国大学生算法设计暑期联赛(1)1011 决斗
思路和官解差不多。
颜色这东西相当于维度,所以这些修改操作大概能等价于维度半平面,大概能规约到和 tb5 等难吧,但我没细想,反正肯定要分块(
答案需要输出每种的颜色的出现次数,考虑分块,维护出连续整块的答案,暴力消掉散块的贡献。暴力抵消的部分需要维护每种颜色连续整块内出现次数,可以差分为前缀整块出现次数。
设块长为 \(B\),两部分预处理复杂度为 \(O(\frac{n^2}{B})\)。
考虑修改,第一种用颜色段均摊等价为 \(O(n+m)\) 次区间修改,第一部分可以暴力,单次 \(O(\frac{n^2}{B^2})\),第二部分其实也是暴力,单次 \(O(\frac{n}{B})\)。
第二种容易想到启发式合并,拆分成 \(O(n+m)\log (n+m)\) 次 区间修改,第二部分还可以直接做,但第一部分只需要对 \(2m\) 种颜色做,所以修改可以减少为 \(O(m)\) 次。
查询复杂度为 \(O(B)\)。
所以总复杂度为 \(O(\frac{n^2}{B}+(n+m)\log (n+m)\frac{n}{B}+m\frac{n^2}{B^2}+mB)\),在 \(B=n^{\frac{2}{3}}\) 时取到理论最优复杂度为 \(O(n^{\frac{5}{3}}+mn^{\frac{2}{3}}+(n+m)n^{\frac{1}{3}}\log (n+m))\),可以通过。
实现时需要注意第二种操作要用类似懒标记的方式记录颜色真实值。
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=2e5+5;
const int B=2005;
// const int B=3;
int C[N][55];
int T, n, m, K, mod;
int a[N];
int pos[N], lp[N], rp[N];
int occ[N][55];
ll sum[55][55];
set<pii> vec[N];
int cnt[N];
int rt[N], idx;
void add1(int col, int l, int r){
int sum=0;
for(int i=1; i<=pos[n]; ++i){
int cl=max(lp[i], l), cr=min(rp[i], r);
sum+=max(0, cr-cl+1);
occ[col][i]+=sum;
}
}
void del1(int col, int l, int r){
int sum=0;
for(int i=1; i<=pos[n]; ++i){
int cl=max(lp[i], l), cr=min(rp[i], r);
sum+=max(0, cr-cl+1);
occ[col][i]-=sum;
}
}
void add2(int col){
for(int i=1; i<=pos[n]; ++i){
for(int j=i; j<=pos[n]; ++j){
sum[i][j]+=C[occ[col][j]-occ[col][i-1]][K];
}
}
}
void del2(int col){
for(int i=1; i<=pos[n]; ++i){
for(int j=i; j<=pos[n]; ++j){
sum[i][j]-=C[occ[col][j]-occ[col][i-1]][K];
}
}
}
void solve(){
read(n); read(m); read(K); read(mod);
C[0][0]=1%mod;
for(int i=1; i<=n; ++i){
C[i][0]=1%mod;
for(int j=1; j<=K; ++j){
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
for(int i=1; i<=n; ++i) pos[i]=(i-1)/B+1;
for(int i=1; i<=pos[n]; ++i) lp[i]=rp[i-1]+1, rp[i]=rp[i-1]+B;
rp[pos[n]]=n;
set<pii> st;
for(int i=1; i<=n+m; ++i) vec[i].clear();
for(int i=1; i<=n; ++i) read(a[i]), st.insert(mapa(i, i)), vec[a[i]].insert(mapa(i, i));
for(int i=1; i<=n+m; ++i) rt[i]=i;
idx=n;
for(int i=1; i<=pos[n]; ++i){
for(int j=1; j<=n+m; ++j) occ[j][i]=occ[j][i-1];
for(int j=lp[i]; j<=rp[i]; ++j) occ[a[j]][i]++;
}
for(int i=1; i<=pos[n]; ++i){
sum[i][i-1]=0;
for(int j=i; j<=pos[n]; ++j){
sum[i][j]=sum[i][j-1];
for(int k=lp[j]; k<=rp[j]; ++k){
sum[i][j]-=C[cnt[a[k]]][K];
++cnt[a[k]];
sum[i][j]+=C[cnt[a[k]]][K];
}
}
for(int j=1; j<=n+m; ++j) cnt[j]=0;
}
while(m--){
int tp;
read(tp);
if(tp==1){
int l, r, c; read(l); read(r); read(c); c=rt[c];
set<pii>::iterator it=st.upper_bound(mapa(l, n)); --it;
if((*it).fi<l){
int tl=(*it).fi, tr=(*it).se;
a[l]=a[tl];
vec[a[l]].erase(*it);
st.erase(it);
st.insert(mapa(tl, l-1)); st.insert(mapa(l, tr));
vec[a[l]].insert(mapa(tl, l-1)); vec[a[l]].insert(mapa(l, tr));
}
it=st.upper_bound(mapa(r, n)); --it;
if((*it).se>r){
int tl=(*it).fi, tr=(*it).se;
a[r+1]=a[tl];
vec[a[r+1]].erase(*it);
st.erase(it);
st.insert(mapa(tl, r)); st.insert(mapa(r+1, tr));
vec[a[r+1]].insert(mapa(tl, r)); vec[a[r+1]].insert(mapa(r+1, tr));
}
it=st.lower_bound(mapa(l, 0)); set<pii>::iterator lit=it;
while(it!=st.end()){
if((*it).fi>r) break;
vec[a[(*it).fi]].erase(*it);
del2(a[(*it).fi]);
del1(a[(*it).fi], (*it).fi, (*it).se);
add2(a[(*it).fi]);
++it;
}
st.erase(lit, it);
st.insert(mapa(l, r));
a[l]=c;
vec[c].insert(mapa(l, r));
del2(c);
add1(c, l, r);
add2(c);
}
else if(tp==2){
int c1, c2; read(c1); read(c2);
int r1=rt[c1], r2=rt[c2];
if(r1==r2) {
rt[c1]=++idx; rt[c2]=r1;
continue;
}
if(vec[r1].size()<vec[r2].size()) swap(r1, r2);
del2(r1); del2(r2);
for(auto t:vec[r2]){
del1(r2, t.fi, t.se);
a[t.fi]=r1;
add1(r1, t.fi, t.se);
vec[r1].insert(t);
}
add2(r1); add2(r2);
vec[r2].clear();
rt[c1]=++idx; rt[c2]=r1;
}
else if(tp==3){
int l, r; read(l); read(r);
ll ans=sum[pos[l]][pos[r]];
unordered_map<int, int> h;
int ml=l, mr=r;
{
l=lp[pos[ml]]; r=ml-1;
if(l<=r){
set<pii>::iterator it=st.upper_bound(mapa(l, n)); --it;
if((*it).fi<l){
int tl=(*it).fi, tr=(*it).se;
a[l]=a[tl];
vec[a[l]].erase(*it);
st.erase(it);
st.insert(mapa(tl, l-1)); st.insert(mapa(l, tr));
vec[a[l]].insert(mapa(tl, l-1)); vec[a[l]].insert(mapa(l, tr));
}
it=st.upper_bound(mapa(r, n)); --it;
if((*it).se>r){
int tl=(*it).fi, tr=(*it).se;
a[r+1]=a[tl];
vec[a[r+1]].erase(*it);
st.erase(it);
st.insert(mapa(tl, r)); st.insert(mapa(r+1, tr));
vec[a[r+1]].insert(mapa(tl, r)); vec[a[r+1]].insert(mapa(r+1, tr));
}
it=st.lower_bound(mapa(l, 0));
while(it!=st.end()){
if((*it).fi>r) break;
h[a[(*it).fi]]+=(*it).se-(*it).fi+1;
++it;
}
}
}
{
l=mr+1; r=rp[pos[mr]];
if(l<=r){
set<pii>::iterator it=st.upper_bound(mapa(l, n)); --it;
if((*it).fi<l){
int tl=(*it).fi, tr=(*it).se;
a[l]=a[tl];
vec[a[l]].erase(*it);
st.erase(it);
st.insert(mapa(tl, l-1)); st.insert(mapa(l, tr));
vec[a[l]].insert(mapa(tl, l-1)); vec[a[l]].insert(mapa(l, tr));
}
it=st.upper_bound(mapa(r, n)); --it;
if((*it).se>r){
int tl=(*it).fi, tr=(*it).se;
a[r+1]=a[tl];
vec[a[r+1]].erase(*it);
st.erase(it);
st.insert(mapa(tl, r)); st.insert(mapa(r+1, tr));
vec[a[r+1]].insert(mapa(tl, r)); vec[a[r+1]].insert(mapa(r+1, tr));
}
it=st.lower_bound(mapa(l, 0));
while(it!=st.end()){
if((*it).fi>r) break;
h[a[(*it).fi]]+=(*it).se-(*it).fi+1;
++it;
}
}
}
l=ml; r=mr;
for(auto t:h) ans-=C[occ[t.fi][pos[r]]-occ[t.fi][pos[l]-1]][K], ans+=C[occ[t.fi][pos[r]]-occ[t.fi][pos[l]-1]-t.se][K];
printf("%lld\n", ans%mod);
}
}
}
int main(){
// freopen("D:\\nya\\acm\\B\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\B\\test.out","w",stdout);
read(T);
while(T--){
solve();
}
return 0;
}
- bonus
我觉得暴力修第一部分有点蠢,可能有更好的做法。
2025“钉耙编程”中国大学生算法设计暑期联赛(2)1004 子串的故事(2)
只是记录一个 parent 树上的 trick,虽然我高一的时候就见过了,提醒一下自己。
用倍增定位子串定位到的是包含这个子串的状态节点,如果直接做 ddp 会有非常多的细节讨论。
考虑 parent 树实际上是反串后缀树,于是可以建虚树,当前前提是可以离线。
建出虚树后每个节点的状态都是取满的,讨论量就大大减少了。
我高一的时候做的题版本是只加入这个区间本身,所以 ddp 好像只需要维护 \(3\times 3\) 的矩阵,但本题是要把每个前缀都加入,所以 ddp 会复杂一些。
使用人工智能或者智能工人可以推出 \(6\times 6\) 的矩阵。
于是直接树剖维护 ddp 即可。
复杂度为 \(O(n\log n6^{3})\)。
一个逆天的点是杭电的老爷机系统栈空间非常小,所以 dfs 次数不能太多,所以本文重点仅在于离线减少讨论的 trick。你无敌了杭电。
代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
x=0;char c=getchar();bool f=0;
for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
x=(f?-x:x);
}
const int N=3e5+6, mod=1e9+7, inv2=(mod+1)/2;
int fpow(ll x, int y){
ll ret=1;
while(y){
if(y&1) ret=ret*x%mod;
x=x*x%mod; y>>=1;
}
return ret;
}
const int inv6=fpow(6, mod-2);
const int inv4=fpow(4, mod-2);
const int inv12=fpow(12, mod-2);
int Test, n, m;
char s[N];
struct node{
int len, c[26], fa;
}sam[N];
int lst, cnt;
vector<int> e[N];
int pos[N];
int f[N][21], sz[N];
inline void extend(int i){
int p=lst, np=lst=++cnt; int x=s[i]-'a';
pos[i]=np;
sam[np].len=sam[p].len+1;
for(; p&&!sam[p].c[x]; p=sam[p].fa) sam[p].c[x]=np;
if(!p) sam[np].fa=1;
else{
int q=sam[p].c[x];
if(sam[q].len==sam[p].len+1) sam[np].fa=q;
else{
int nq=++cnt;
sam[nq]=sam[q];
sam[nq].len=sam[p].len+1;
sam[q].fa=sam[np].fa=nq;
for(; p&&sam[p].c[x]==q; p=sam[p].fa) sam[p].c[x]=nq;
}
}
}
vector<pii> bin[N];
inline void init(){
for(int i=1; i<=cnt; ++i) {
e[i].clear(); bin[i].clear();
sam[i].fa=sam[i].len=0;
for(int t=0; t<26; ++t) sam[i].c[t]=0;
}
cnt=lst=1;
for(int i=1; i<=n; ++i) extend(i);
for(int i=2; i<=cnt; ++i) f[i][0]=sam[i].fa, sz[i]=sam[i].len-sam[sam[i].fa].len;
for(int i=1; i<21; ++i){
for(int j=1; j<=cnt; ++j) f[j][i]=f[f[j][i-1]][i-1];
}
}
inline int locate(int l, int r){
int x=pos[r];
for(int t=20; t>=0; --t) if(sam[f[x][t]].len>=r-l+1) x=f[x][t];
return x;
}
int qpos[N];
int lenid[N];
struct mat{
int a[6][6];
mat(){memset(a, 0, sizeof a);}
bool empty(){
bool ret=1;
for(int i=0; i<6; ++i){
for(int j=0; j<6; ++j){
if(i==j) ret&=a[i][j]==1;
else ret&=a[i][j]==0;
}
}
return ret;
}
}E;
inline mat operator *(mat x, mat y){
mat ret;
for(int k=0; k<6; ++k){
for(int i=0; i<6; ++i) if(x.a[i][k]){
for(int j=0; j<6; ++j){
ret.a[i][j]=(1ll*x.a[i][k]*y.a[k][j]+ret.a[i][j])%mod;
}
}
}
return ret;
}
inline mat operator +(mat x, mat y){
for(int i=0; i<6; ++i){
for(int j=0; j<6; ++j){
x.a[i][j]=(x.a[i][j]+y.a[i][j])%mod;
}
}
return x;
}
mat tr[N<<2], tag[N<<2];
int trsz[N], mxson[N];
void dfs1(int x){
trsz[x]=1; mxson[x]=0;
for(auto y:e[x]){
dfs1(y);
trsz[x]+=trsz[y];
if(trsz[y]>trsz[mxson[x]]) mxson[x]=y;
}
return ;
}
int dfn[N], seq[N], timer, top[N];
void dfs2(int x, int tp){
top[x]=tp; dfn[x]=++timer; seq[timer]=x;
if(mxson[x]) dfs2(mxson[x], tp);
for(auto y:e[x]) if(y^mxson[x]) dfs2(y, y);
return ;
}
void build(int p, int l, int r){
tag[p]=E;
if(l==r){
tr[p]=tr[0];
int x=seq[l];
tr[p].a[0][0]=1ll*inv2*sz[x]%mod;
tr[p].a[0][1]=(1ll*inv2*sz[x]%mod*sz[x]%mod+mod-1ll*sz[x]*sam[x].len%mod)%mod;
tr[p].a[0][2]=(1ll*inv2*sz[x]%mod*sam[x].len%mod*sam[x].len%mod+1ll*inv2*sz[x]%mod*sam[x].len%mod+1ll*inv12*sz[x]%mod*(sz[x]+1)%mod*(2*sz[x]+1)%mod+mod+mod-1ll*inv4*sz[x]%mod*(sz[x]+1)%mod-1ll*inv2*sz[x]%mod*(sz[x]+1)%mod*sam[x].len%mod)%mod;
tr[p].a[0][3]=(1ll*inv2*sz[x]*(sz[x]+1)%mod+mod-1ll*sam[x].len*sz[x]%mod)%mod;
tr[p].a[0][4]=(1ll*inv6*sz[x]%mod*(sz[x]+1)%mod*(2*sz[x]+1)%mod+1ll*sam[x].len*sam[x].len%mod*sz[x]%mod+mod-1ll*sz[x]*sam[x].len%mod*(sz[x]+1)%mod)%mod;
return ;
}
int mid=(l+r)>>1;
build(p<<1, l, mid); build(p<<1|1, mid+1, r);
tr[p]=tr[p<<1]+tr[p<<1|1];
}
mat curtag;
void down(int p){
if(!tag[p].empty()){
tag[p<<1]=tag[p<<1]*tag[p]; tr[p<<1]=tr[p<<1]*tag[p];
tag[p<<1|1]=tag[p<<1|1]*tag[p]; tr[p<<1|1]=tr[p<<1|1]*tag[p];
tag[p]=E;
}
}
void mdf(int p, int l, int r, int L, int R){
if(L<=l&&r<=R){
tag[p]=tag[p]*curtag;
tr[p]=tr[p]*curtag;
return ;
}
int mid=(l+r)>>1;
down(p);
if(L<=mid) mdf(p<<1, l, mid, L, R);
if(R>mid) mdf(p<<1|1, mid+1, r, L, R);
tr[p]=tr[p<<1]+tr[p<<1|1];
}
void solve(){
read(n); read(m);
scanf("%s", s+1);
reverse(s+1, s+n+1);
init();
for(int i=1, l, r; i<=m; ++i) {
read(l); read(r);
int cur=locate(n-r+1, n-l+1);
bin[cur].ep(r-l+1, i);
}
int idx=cnt;
for(int i=2; i<=cnt; ++i){
if(bin[i].empty()) continue;
vector<int> tem;
for(auto t:bin[i]) tem.ep(t.fi);
sort(tem.begin(), tem.end());
tem.erase(unique(tem.begin(), tem.end()), tem.end());
if(tem.back()==sam[i].len) tem.pop_back();
int lstfa=sam[i].fa;
for(auto t:tem){
++idx;
sam[idx].len=t; lenid[t]=idx;
sam[idx].fa=lstfa;
lstfa=idx;
}
lenid[sam[i].len]=i;
sam[i].fa=lstfa;
for(auto t:bin[i]){
qpos[t.se]=lenid[t.fi];
}
}
for(int i=1; i<=idx; ++i) e[i].clear();
for(int i=2; i<=idx; ++i) e[sam[i].fa].emplace_back(i), sz[i]=sam[i].len-sam[sam[i].fa].len;
dfs1(1);
timer=0;
dfs2(1, 1);
build(1, 1, idx);
for(int i=1; i<=m; ++i){
curtag=E;
curtag.a[3][1]=curtag.a[4][2]=curtag.a[2][5]=1;
curtag.a[0][1]=2*sam[qpos[i]].len;
curtag.a[3][2]=curtag.a[1][5]=sam[qpos[i]].len;
curtag.a[0][5]=1ll*sam[qpos[i]].len*sam[qpos[i]].len%mod;
int x=qpos[i];
while(x){
mdf(1, 1, idx, dfn[top[x]], dfn[x]);
x=sam[top[x]].fa;
}
printf("%d\n", tr[1].a[0][5]);
}
}
int main(){
// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
// freopen("D:\\nya\\acm\\A\\test2.out","w",stdout);
E.a[0][0]=E.a[1][1]=E.a[2][2]=E.a[3][3]=E.a[4][4]=E.a[5][5]=1;
read(Test);
while(Test--){
solve();
}
return 0;
}

浙公网安备 33010602011771号