杂题选做 Ⅱ
优秀的拆分
实际上只需要求以 \(i\) 为起始位置的 \(AA\) 串的长度。
枚举串 \(A\) 长度 \(len\),定长分块,每隔 \(len\) 撒一个点,则一个 \(AA\) 串必会经过相邻的两个点。
那么对于此时每一对相邻的点,求他们对应前后缀的的 \(lcs,lcp\) 就能算出来经过这两个点长为 \(len\) 的 \(AA\) 串的起始下标的区间,差分后一起处理即可。
枚举复杂度是调和级数,瓶颈在后缀数组,总复杂度 \(O(n\log n)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define i28 __int128
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=3e4+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
struct SA{
char s[N];
int len,tot,lg[N];
int sa[N],rk[N],h[N];
int tmp[N],bin[N],siz;
void clear(){
for(int i=1;i<=len;++i) s[i]='\0';
memset(sa,0,sizeof sa);
memset(rk,0,sizeof rk);
memset(h,0,sizeof h);
memset(tmp,0,sizeof tmp);
memset(bin,0,sizeof bin);
}
void qsort(){
for(int i=0;i<=siz;++i) bin[i]=0;
for(int i=1;i<=len;++i) bin[rk[tmp[i]]]++;
for(int i=1;i<=siz;++i) bin[i]+=bin[i-1];
for(int i=len;i>=1;--i) sa[bin[rk[tmp[i]]]--]=tmp[i];
}
void build(){
len=strlen(s+1);
siz=max(len,'z'-'0'+1);
for(int i=1;i<=len;++i)
tmp[i]=i,rk[i]=s[i]-'0'+1;
qsort();
for(int j=1;j<=len;j<<=1){
tot=0;
for(int i=len-j+1;i<=len;++i) tmp[++tot]=i;
for(int i=1;i<=len;++i)
if(sa[i]-j>0)
tmp[++tot]=sa[i]-j;
qsort();
swap(tmp,rk);
rk[sa[1]]=tot=1;
for(int i=2;i<=len;++i){
if(tmp[sa[i]]==tmp[sa[i-1]] &&
tmp[sa[i]+j]==tmp[sa[i-1]+j])
rk[sa[i]]=tot;
else rk[sa[i]]=++tot;
}
if(tot==len) break;
}
}
int calc(int x,int y,int tlen){
while(x+tlen<=len && y+tlen<=len &&
s[x+tlen]==s[y+tlen]) ++tlen;
return tlen;
}
void build_h(){
for(int i=1;i<=len;++i)
h[i]=(rk[i]==1)?0:calc(i,sa[rk[i]-1],max(h[i-1]-1,0));
}
int st[22][N];
void build_st(){
lg[0]=-1;
for(int i=1;i<=len;++i) lg[i]=lg[i>>1]+1;
for(int i=1;i<=len;++i) st[0][i]=h[sa[i]];
for(int j=1;(1<<j)<=len;++j){
int step=1<<(j-1);
for(int i=1;i+step<=len;++i){
st[j][i]=min(st[j-1][i],st[j-1][i+step]);
}
}
}
int lcp(int x,int y){
if(x==y) return len-x+1;
x=rk[x],y=rk[y];
if(x>y)swap(x,y); ++x;
int step=lg[y-x+1];
return min(st[step][x],st[step][y-(1<<step)+1]);
}
} pre,suf;
char s[N],tmp[N];
int n,a[N],b[N],d[N];
void solve(){
for(int i=1;i<=n;++i) d[i]=0;
suf.clear(); pre.clear();
for(int i=1;i<=n;++i){
suf.s[i]=s[i];
pre.s[i]=s[n-i+1];
}
suf.build(); suf.build_h(); suf.build_st();
pre.build(); pre.build_h(); pre.build_st();
for(int len=1;len<=n;++len){
for(int i=len,j;i<=n;i+=len){
j=i+len;
if(j>n) break;
if(s[i]!=s[j]) continue;
int L=pre.lcp(n-i+1,n-j+1),R=suf.lcp(i,j);
int ul=i-L+1,ur=i+R-1;
chkmax(ul,i-len+1);
chkmin(ur,j-1);
if(ul+len-1>ur) continue;
++d[ul]; --d[ur-len+2];
}
}
for(int i=1;i<=n;++i) d[i]+=d[i-1];
for(int i=1;i<=n;++i) a[i]=d[i];
}
void Mian(){
cin>>s+1;
n=strlen(s+1);
solve();
for(int i=1;i<=n;++i) b[i]=a[i];
for(int i=1;i<=n;++i) tmp[i]=s[i];
for(int i=1;i<=n;++i) s[i]=tmp[n-i+1];
solve();
ll ans=0;
for(int i=1;i<n;++i){
ans+=1ll*a[n-i+1]*b[i+1];
}
cout<<ans<<'\n';
//for(int i=1;i<=n;++i) cout<<a[i]<<' '<<b[i]<<'\n'; cout<<'\n'<<'\n';
}
void Mianclr(){
for(int i=1;i<=n;++i) s[i]='\0',a[i]=b[i]=0;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; cin>>T;
while(T--){
Mian();
Mianclr();
}
}
拦截导弹
设 \(len_i\) 为以 \(i\) 为结尾的最长子序列,\(cnt_i\) 为此时以 \(i\) 结尾最长子序列个数,有转移:
其中要求 \(j < i,h_j \geq h_i,v_j \geq v_i\)。
直接转移会爆,发现限制是一个三维偏序,我们可以使用 cdq 分治维护。
求出现概率也很简单,那就是包含 \(i\) 的最长子序列和总最长子序列数的比值。我们对称记一个反的 \(len,cnt\),则如果正反 \(len\) 加起来是最长的,那么包含 \(i\) 的最长子序列数就是两个 \(cnt\) 的乘积。
注意有可能爆 long long,要用 double。以及统计总最长子序列数的时候不要重复计数。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define i28 __int128
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=1e5+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
#define pid pair<int,long double>
int n,h[N],v[N],b[N],tot,ans;
struct BIT1{
pid f[N];
void clear(int u){
for(;u<=tot;u+=lowbit(u)) f[u]={0,0.0};
}
void modify(int u,pid d){
for(;u<=tot;u+=lowbit(u)){
if(d.fir>f[u].fir) f[u]=d;
else if(d.fir==f[u].fir) f[u].sec+=d.sec;
}
}
pid query(int u){
pid ret={0,0.0};
for(;u;u-=lowbit(u)){
if(f[u].fir>ret.fir) ret=f[u];
else if(f[u].fir==ret.fir) ret.sec+=f[u].sec;
}
return ret;
}
} T;
pid p[N],s[N];
struct node{
int id,h,v,tag;
} a[N],tmp[N];
bool cmp1(node u,node v){
return u.id<v.id;
}
bool cmp2(node u,node v){
return u.h>v.h;
}
// a - h down
void cdq1(int l,int r,pid *dp){
if(l==r) return ;
int mid=l+r>>1;
cdq1(l,mid,dp);
for(int i=l;i<=r;++i) tmp[i]=a[i];
sort(a+l,a+mid+1,cmp2);
sort(a+mid+1,a+r+1,cmp2);
for(int i=mid+1,j=l;i<=r;++i){
while(j<=mid && a[j].h>=a[i].h){
T.modify(tot-a[j].v+1,dp[a[j].id]);
++j;
}
pid uu=T.query(tot-a[i].v+1);
if(uu.fir<=0) continue;
uu.fir+=1;
if(uu.fir>dp[a[i].id].fir) dp[a[i].id]=uu;
else if(uu.fir==dp[a[i].id].fir) dp[a[i].id].sec+=uu.sec;
}
for(int j=l;j<=mid;++j) T.clear(tot-a[j].v+1);
for(int i=l;i<=r;++i) a[i]=tmp[i];
cdq1(mid+1,r,dp);
}
// a - h up
void cdq2(int l,int r,pid *dp){
if(l==r) return ;
int mid=l+r>>1;
cdq2(mid+1,r,dp);
for(int i=l;i<=r;++i) tmp[i]=a[i];
sort(a+l,a+mid+1,cmp2); reverse(a+l,a+mid+1);
sort(a+mid+1,a+r+1,cmp2); reverse(a+mid+1,a+r+1);
for(int i=l,j=mid+1;i<=mid;++i){
while(j<=r && a[j].h<=a[i].h){
T.modify(a[j].v,dp[a[j].id]);
++j;
}
pid uu=T.query(a[i].v);
if(uu.fir<=0) continue;
uu.fir+=1;
if(uu.fir>dp[a[i].id].fir) dp[a[i].id]=uu;
else if(uu.fir==dp[a[i].id].fir) dp[a[i].id].sec+=uu.sec;
}
for(int j=mid+1;j<=r;++j) T.clear(a[j].v);
for(int i=l;i<=r;++i) a[i]=tmp[i];
cdq2(l,mid,dp);
}
long double sum;
void Mian(){
cin>>n;
for(int i=1;i<=n;++i) cin>>h[i]>>v[i];
for(int i=1;i<=n;++i) b[++tot]=h[i],b[++tot]=v[i];
sort(b+1,b+tot+1);
tot=unique(b+1,b+tot+1)-b-1;
for(int i=1;i<=n;++i){
h[i]=lower_bound(b+1,b+tot+1,h[i])-b;
v[i]=lower_bound(b+1,b+tot+1,v[i])-b;
}
for(int i=1;i<=n;++i){
a[i].id=i;
a[i].h=h[i]; a[i].v=v[i];
}
for(int i=1;i<=n;++i) p[i]={1,1.0};
cdq1(1,n,p);
//for(int i=1;i<=n;++i) cout<<lenp[i]<<' '; cout<<'\n';
for(int i=1;i<=n;++i){
a[i].id=i;
a[i].h=h[i]; a[i].v=v[i];
}
for(int i=1;i<=n;++i) s[i]={1,1.0};
cdq2(1,n,s);
//for(int i=1;i<=n;++i) cout<<lens[i]<<' '; cout<<'\n';
for(int i=1;i<=n;++i) chkmax(ans,p[i].fir+s[i].fir-1);
printf("%d\n",ans);
for(int i=1;i<=n;++i){
if(p[i].fir==ans)
sum+=p[i].sec;
}
for(int i=1;i<=n;++i){
if(p[i].fir+s[i].fir-1==ans)
printf("%.5Lf ",p[i].sec*s[i].sec/sum);
else printf("0 ");
}
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
DZY Loves Chinese
这题真神了。
强制在线,注意到输入的边集大小也要异或上前面的值,所以可以直接判后面输入了多少个数来得到上一组数据的答案。
所以只用暴力最后一组就行了。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=5e5+9,M=1e6+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,m,q,lst,o[N];
int fa[N],siz[N];
int find(int u){
return fa[u]==u?u:fa[u]=find(fa[u]);
}
void merge(int u,int v){
u=find(u); v=find(v);
if(u==v) return ;
fa[v]=u; siz[u]+=siz[v];
}
pii a[N];
bool used[N];
void Mian(){
cin>>n>>m;
for(int i=1;i<=m;++i) cin>>a[i].fir>>a[i].sec;
cin>>q; cin.ignore();
for(int i=1;i<q;++i){
string s;
getline(cin,s);
int k=0,cnt=0;
for(int j=0;j<s.size();++j){
if(isdigit(s[j])){
k=k*10+s[j]-'0';
}else break;
}
for(int j=0;j<s.size();++j)
if(!isdigit(s[j])) ++cnt; int tk=k;
k^=lst;
if(i==1) continue;
if(k!=cnt){
puts("Connected");
lst++;
}else puts("Disconnected");
// cout<<i<<' '<<tk<<' '<<lst<<' '<<cnt<<"!!!\n";
}
int k; cin>>k; k^=lst;
int x,cnt=0; vector<int> tmp;
while(cin>>x){
tmp.pb(x); ++cnt;
}
if(k!=cnt){
puts("Connected");
++lst;
}
else puts("Disconnected");
// cout<<lst<<"???"<<endl;
for(auto i:tmp) used[i^lst]=1;
for(int i=1;i<=n;++i) fa[i]=i,siz[i]=1;
for(int i=1;i<=m;++i){
if(used[i]) continue;
merge(a[i].fir,a[i].sec);
}
if(siz[find(1)]==n) puts("Connected");
else puts("Disconnected");
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
DZY Loves Chinese II
神题这真了。
具体做法证明在这里,推荐看一下,以下展示笔者在做此题时的思路。
由于只考虑连通性,我们还是可以取出一棵 dfs 树考虑,那么不连通的条件就是存在一个树边,这条边和所有跨越这条边的非树边都被删除。
做过 wmr 的哈希智慧题,我们考虑异或哈希,对非树边随机赋权,树边就是所有跨过这条边的非树边的异或和,查询即为判断是否存在一个子集对应边权异或和为 \(0\)。上线性基就做完了。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=1e5+9,M=5e5+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
mt19937 rd(random_device{}());
int h[39];
void init(){
for(int j=0;j<=31;++j) h[j]=0;
}
int insert(int x){
for(int j=31;j>=0;--j){
if(!((x>>j)&1)) continue;
if(h[j]){x^=h[j]; continue;}
h[j]=x;
return 0;
}
if(!x) return 1;
}
int n,m,q,a[M],dep[N];
pii e[M];
bool intr[M];
vector<pii> G[N];
void pdfs(int u,int fa){
dep[u]=dep[fa]+1;
for(auto y:G[u]){
int v=y.fir,id=y.sec;
if(v==fa) continue;
if(!dep[v]){
pdfs(v,u);
intr[id]=1;
}
}
}
int dfs(int u,int fa){
int w=0;
for(auto y:G[u]){
int v=y.fir,id=y.sec;
if(v==fa) continue;
if(intr[id]) w^=dfs(v,u);
else w^=a[id];
}
for(auto y:G[u]){
int v=y.fir,id=y.sec;
if(v==fa) a[id]=w;
}
return w;
}
void Mian(){
cin>>n>>m;
for(int i=1;i<=m;++i){
int u,v; cin>>u>>v;
e[i]={u,v};
G[u].pb({v,i}); G[v].pb({u,i});
}
pdfs(1,0);
for(int i=1;i<=m;++i) if(!intr[i]) a[i]=rd();
dfs(1,0);
cin>>q;
int lstans=0;
while(q--){
init();
int k,flg=0; cin>>k;
while(k--){
int o; cin>>o;
flg|=insert(a[o^lstans]);
}
if(flg) puts("Disconnected");
else{
puts("Connected");
++lstans;
}
}
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
切割
上一个题的双倍经验,注意重边的处理就行。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=1e6+9,M=1e6+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
mt19937 rd(random_device{}());
int h[39];
void init(){
for(int j=0;j<=31;++j) h[j]=0;
}
int insert(int x){
for(int j=31;j>=0;--j){
if(!((x>>j)&1)) continue;
if(h[j]){x^=h[j]; continue;}
h[j]=x;
return 0;
}
if(!x) return 1;
}
int n,m,q,a[M],dep[N];
pii e[M];
bool intr[M];
vector<pii> G[N];
void pdfs(int u,int fa,int fid){
dep[u]=dep[fa]+1;
for(auto y:G[u]){
int v=y.fir,id=y.sec;
if(v==fa) continue;
if(!dep[v]){
pdfs(v,u,id);
intr[id]=1;
}
}
}
int dfs(int u,int fa,int fid){
int w=0;
for(auto y:G[u]){
int v=y.fir,id=y.sec;
if(v==fa){
if(id==fid) continue;
w^=a[id];
continue;
}
if(intr[id]) w^=dfs(v,u,id);
else w^=a[id];
}
for(auto y:G[u]){
int v=y.fir,id=y.sec;
if(id==fid) a[id]=w;
}
return w;
}
void Mian(){
cin>>n>>m;
for(int i=1;i<=m;++i){
int u,v; cin>>u>>v;
e[i]={u,v};
G[u].pb({v,i}); G[v].pb({u,i});
}
pdfs(1,0,0);
for(int i=1;i<=m;++i) if(!intr[i]) a[i]=rd();
dfs(1,0,0);
cin>>q;
while(q--){
init();
int k,flg=0; cin>>k;
while(k--){
int o; cin>>o;
flg|=insert(a[o]);
}
if(flg) puts("Bob");
else puts("ymqOAO");
}
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
命运
经典题了。
设出来 \(dp(u,j)\) 表示 \(u\) 点子树内未满足的限制中对应子树外结点最大深度为 \(j\),转移考虑当前点 \(v\) 到其父亲 \(u\) 的边的颜色,即:
然后跟 Minimax 这个题一样,在线段树合并的时候维护 \(dp\) 值即可。由于一开始只有 \(m\) 个点非空,视 \(n,m\) 同阶,复杂度为 \(O(n\log n)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=5e5+9,M=1e6+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,m,root[N],dep[N],tot;
vector<int> G[N],l[N];
struct node{
int ls,rs;
i64 sum,tag;
} tre[N*80];
void add(i64 &x,i64 y){x=(x+y)%MOD;}
void mul(i64 &x,i64 y){x=(x*y)%MOD;}
void insert(int &u,int l,int r,int x){
u=++tot;
tre[u].sum=tre[u].tag=1;
if(l==r) return ;
int mid=l+r>>1;
if(x<=mid) insert(tre[u].ls,l,mid,x);
if(x>mid) insert(tre[u].rs,mid+1,r,x);
}
void push_down(int u){
if(tre[u].tag==1) return ;
if(tre[u].ls){
mul(tre[tre[u].ls].sum,tre[u].tag);
mul(tre[tre[u].ls].tag,tre[u].tag);
}
if(tre[u].rs){
mul(tre[tre[u].rs].sum,tre[u].tag);
mul(tre[tre[u].rs].tag,tre[u].tag);
}
tre[u].tag=1;
}
int query(int u,int l,int r,int x){
if(!u || l==r) return tre[u].sum;
push_down(u);
int mid=l+r>>1;
if(x<=mid) return query(tre[u].ls,l,mid,x);
if(x>mid) return (tre[tre[u].ls].sum+query(tre[u].rs,mid+1,r,x))%MOD;
}
int merge(int x,int y,int l,int r,i64 &s1,i64 &s2){
if(!x && !y) return 0;
if(!x || !y){
if(!x){
add(s1,tre[y].sum);
mul(tre[y].sum,s2);
mul(tre[y].tag,s2);
return y;
}
add(s2,tre[x].sum);
mul(tre[x].sum,s1);
mul(tre[x].tag,s1);
return x;
}
if(l==r){
i64 tx=tre[x].sum,ty=tre[y].sum;
tre[x].sum=(tx*(s1+ty)+ty*s2)%MOD;
add(s1,ty); add(s2,tx);
return x;
}
push_down(x); push_down(y);
int mid=l+r>>1;
tre[x].ls=merge(tre[x].ls,tre[y].ls,l,mid,s1,s2);
tre[x].rs=merge(tre[x].rs,tre[y].rs,mid+1,r,s1,s2);
tre[x].sum=(tre[tre[x].ls].sum+tre[tre[x].rs].sum)%MOD;
return x;
}
void dfs(int u,int fa){
dep[u]=dep[fa]+1;
int mxdep=0;
for(auto v:l[u]) chkmax(mxdep,dep[v]);
insert(root[u],0,n,mxdep);
for(auto v:G[u]){
if(v==fa) continue;
dfs(v,u);
i64 s1=query(root[v],0,n,dep[u]),s2=0;
root[u]=merge(root[u],root[v],0,n,s1,s2);
}
}
void Mian(){
cin>>n;
for(int i=1;i<n;++i){
int u,v; cin>>u>>v;
G[u].pb(v); G[v].pb(u);
}
cin>>m;
for(int i=1;i<=m;++i){
int u,v; cin>>u>>v;
l[v].pb(u);
}
dfs(1,0);
cout<<query(root[1],0,n,0);
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
如何正确地排序
好题啊!!1
笔者的做法是把 max 和 min 拆开。假设现在要算 min,考虑一个 \(a_{i,j}\) 什么时候有贡献,然后会发现限制可以写成一个三维偏序的形式。
然后直接跑三维偏序就是对的,注意这里要去重。
另一个 比较高妙的做法 是把其中的 min 用 min-max 容斥拆开,和 max 正好抵消,这样就只需要二维偏序了。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=2e5+9,M=1e6+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
#define int long long
int m,n,r[5][N],tot,ans,D=2e5+5;
struct BIT{
int f[(int)4e5+9],up=(4e5);
void modify(int u,int val){
for(;u<=up;u+=lowbit(u))
f[u]+=val;
}
int query(int u){
int ret=0;
for(;u;u-=lowbit(u))
ret+=f[u];
return ret;
}
} T;
struct node{
int op,a,b,c,w;
} a[N<<4],tmp[N<<4];
bool cmp(node u,node v){
return u.a!=v.a?u.a<v.a:u.op<v.op;
}
void cdq(int l,int r){
if(l==r) return ;
int mid=l+r>>1;
cdq(l,mid); cdq(mid+1,r);
int p=l-1,i=mid+1,j=l;
for(;i<=r;++i){
while(j<=mid && a[j].b<=a[i].b){
if(!a[j].op) T.modify(a[j].c,1);
tmp[++p]=a[j++];
}
if(a[i].op) ans+=a[i].w*T.query(a[i].c);
tmp[++p]=a[i];
}
for(int o=l;o<j;++o)
if(!a[o].op) T.modify(a[o].c,-1);
while(j<=mid) tmp[++p]=a[j++];
for(int o=l;o<=r;++o) a[o]=tmp[o];
}
void Mian(){
cin>>m>>n;
for(int i=1;i<=m;++i)
for(int j=1;j<=n;++j) cin>>r[i][j];
for(int i=m+1;i<=4;++i)
for(int j=1;j<=n;++j) r[i][j]=(2e5+1);
for(int k=1;k<=m;++k){
tot=0;
for(int i=1;i<=n;++i){
int t1[4],t2[4],tc=0;
for(int j=1;j<=4;++j){
if(j<k) t1[++tc]=r[k][i]-r[j][i],t2[tc]=r[j][i]-r[k][i]-1;
if(j>k) t1[++tc]=r[k][i]-r[j][i],t2[tc]=r[j][i]-r[k][i];
}
a[++tot]={0,t1[1]+D,t1[2]+D,t1[3]+D,r[k][i]};
a[++tot]={1,t2[1]+D,t2[2]+D,t2[3]+D,r[k][i]};
}
sort(a+1,a+tot+1,cmp);
cdq(1,tot);
}
for(int i=m+1;i<=4;++i)
for(int j=1;j<=n;++j) r[i][j]=-1;
for(int k=1;k<=m;++k){
tot=0;
for(int i=1;i<=n;++i){
int t1[4],t2[4],tc=0;
for(int j=1;j<=4;++j){
if(j<k) t1[++tc]=r[k][i]-r[j][i],t2[tc]=r[j][i]-r[k][i]+1;
if(j>k) t1[++tc]=r[k][i]-r[j][i],t2[tc]=r[j][i]-r[k][i];
}
a[++tot]={1,t1[1]+D,t1[2]+D,t1[3]+D,r[k][i]};
a[++tot]={0,t2[1]+D,t2[2]+D,t2[3]+D,r[k][i]};
}
sort(a+1,a+tot+1,cmp);
cdq(1,tot);
}
cout<<ans*2;
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
接水果
哎呀这个要求怎么是盘子是水果的子路径,太坏了。
把限制用 dfs 序刻画后直接上整体二分就是对的,复杂度两个 log。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define i28 __int128
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=2e5+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,p,q,dfn[N],siz[N],idx,tot;
int ans[N],f[20][N],dep[N];
struct node{
int x,L,R;
int op,k,id;
bool operator<(const node &res)const{
return x==res.x?op<res.op:x<res.x;
}
} a[N];
vector<int> G[N];
void dfs(int u,int fa){
f[0][u]=fa; dfn[u]=++idx;
siz[u]=1; dep[u]=dep[fa]+1;
for(int j=1;j<=19;++j)
f[j][u]=f[j-1][f[j-1][u]];
for(auto v:G[u]){
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
}
}
int lca(int u,int v){
if(dep[u]>dep[v]) swap(u,v);
for(int j=19;j>=0;--j){
if(dep[f[j][v]]>=dep[u])
v=f[j][v];
}
if(u==v) return u;
for(int j=19;j>=0;--j)
if(f[j][u]!=f[j][v]) u=f[j][u],v=f[j][v];
return f[0][u];
}
int getson(int u,int v){
for(int j=19;j>=0;--j){
if(dep[f[j][v]]>dep[u])
v=f[j][v];
}
return v;
}
struct BIT{
int f[N];
void modify(int u,int d){
for(;u<=n;u+=lowbit(u))
f[u]+=d;
}
int query(int u){
int ret=0;
for(;u;u-=lowbit(u)) ret+=f[u];
return ret;
}
} T;
void solve(vector<int> &Q,int l,int r){
if(l>r || !Q.size()) return ;
if(l==r){
for(auto y:Q)
if(a[y].op==2) ans[a[y].id]=l;
return ;
}
int mid=(l+r)>>1;
vector<int> L,R;
for(auto y:Q){
if(a[y].op==1){
if(abs(a[y].k)<=mid){
int coef=a[y].k/abs(a[y].k);
T.modify(a[y].L,1*coef);
T.modify(a[y].R+1,-1*coef);
L.pb(y);
}else R.pb(y);
}
if(a[y].op==2){
int ret=T.query(a[y].L);
if(a[y].k<=ret) L.pb(y);
else{
a[y].k-=ret;
R.pb(y);
}
}
}
for(auto y:Q){
if(a[y].op==1){
if(abs(a[y].k)<=mid){
int coef=a[y].k/abs(a[y].k);
T.modify(a[y].L,-1*coef);
T.modify(a[y].R+1,1*coef);
}
}
}
solve(L,l,mid); solve(R,mid+1,r);
}
void Mian(){
cin>>n>>p>>q;
for(int i=1;i<n;++i){
int u,v; cin>>u>>v;
G[u].pb(v); G[v].pb(u);
}
dfs(1,0);
for(int i=1;i<=p;++i){
int u,v,k; cin>>u>>v>>k; ++k;
if(dfn[u]>dfn[v]) swap(u,v);
if(lca(u,v)==u){
u=getson(u,v);
if(dfn[u]>1){
a[++tot]={1,dfn[v],dfn[v]+siz[v]-1,1,k,0};
a[++tot]={dfn[u],dfn[v],dfn[v]+siz[v]-1,1,-k,0};
}
if(dfn[u]+siz[u]-1<n){
a[++tot]={dfn[v],dfn[u]+siz[u],n,1,k,0};
a[++tot]={dfn[v]+siz[v],dfn[u]+siz[u],n,1,-k,0};
}
}else{
a[++tot]={dfn[u],dfn[v],dfn[v]+siz[v]-1,1,k,0};
a[++tot]={dfn[u]+siz[u],dfn[v],dfn[v]+siz[v]-1,1,-k,0};
}
}
for(int i=1;i<=q;++i){
int u,v,k; cin>>u>>v>>k;
if(dfn[u]>dfn[v]) swap(u,v);
a[++tot]={dfn[u],dfn[v],0,2,k,i};
}
sort(a+1,a+tot+1);
vector<int> tmp; for(int i=1;i<=tot;++i) tmp.pb(i);
solve(tmp,1,1000000001);
for(int i=1;i<=q;++i) cout<<ans[i]-1<<'\n';
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
沉玉谷
区间 dp 好题。
一个加深区间 dp 理解的 blog。
由上文,设出 \(f(l,r,k)\) 表示删完 \([l,r]\) 用了 \(k\) 步,考虑最后删的哪一个,设 \(g(i,j,k,x)\) 表示删 \([l,r]\) 的一部分用了 \(k\) 步,其中余下的颜色都是 \(x\),\(g\) 的转移即钦定最后删颜色 \(x\) 时下标最小的那个元素的位置。
复杂度 \(O(n^5)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=50+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,a[N],C[N][N],f[N][N][N],g[N][N][N][N];
void add(int &x,int y){x=(x+y)%MOD;}
void Mian(){
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=0;i<=n;++i){
for(int j=0;j<=i;++j){
if(!j) C[i][j]=1;
else C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}
}
for(int i=1;i<=n;++i)
f[i][i][1]=f[i][i-1][0]=f[i+1][i][0]=1,g[i][i][0][a[i]]=1;
for(int len=2;len<=n;++len){
for(int i=1,j;i+len-1<=n;++i){
j=i+len-1;
for(int p=i;p<=j;++p){
for(int k1=0;k1<=len;++k1) for(int k2=0;k1+k2<=len;++k2){
add(g[i][j][k1+k2][a[p]],1ll*f[i][p-1][k1]*(f[p+1][j][k2]+g[p+1][j][k2][a[p]])%MOD*C[k1+k2][k1]%MOD);
}
}
for(int k=1;k<=len;++k)
for(int x=1;x<=n;++x) add(f[i][j][k],g[i][j][k-1][x]);
}
}
// for(int i=1;i<=n;++i){
// for(int j=i;j<=n;++j){
// for(int k=0;k<=n;++k){
// for(int x=1;x<=n;++x) cout<<i<<' '<<j<<' '<<k<<' '<<x<<' '<<g[i][j][k][x]<<endl;
// }
// }
// }
int ans=0;
for(int i=1;i<=n;++i) add(ans,f[1][n][i]);
cout<<ans;
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
方块消除
依旧区间 dp 好题。
要求的是一个最优化的形式,还是考虑最后一个删除的操作,设 \(f(l,r)\) 表示删除完 \([l,r]\) 中所有方块的最大价值和,\(g(l,r,k,x)\) 表示删除 \([l,r]\) 至只剩 \(x\) 个颜色 \(k\),但是这样 \(g\) 转移的复杂度就爆了。
把 \(k\) 这一维的限制写进 \(l,r\) 中,设 \(g(l,r,x)\) 表示删除 \([l,r]\) 至只剩 \(x\) 个颜色 \(a_l\),这里隐含了钦定 \(a_l\) 是最后一次操作被删的块之一。不难发现这样设计状态一定能包含到所有情况。
\(f\) 转移就是两个删完的并起来或者用 \(g\) 转移最后一次删除。\(g\) 的转移有点复杂,因为我们钦定了 \(a_l\) 最后被删,所以是前面一段 \(g\) 后面一段 \(f\) 或者当 \(a_l=a_r\) 时把它们并起来。
时间复杂度 \(O(m^3V)\),其中 \(V=\sum c_i\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=50+9,M=1000+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,a[N],c[N],f[N][N],g[N][N][M];
void Mian(){
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i) cin>>c[i];
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
for(int x=1;x<=1000;++x) g[i][j][x]=-INF;
for(int i=1;i<=n;++i) f[i][i]=c[i]*c[i],g[i][i][c[i]]=0;
for(int len=2;len<=n;++len){
for(int i=1,j;i+len-1<=n;++i){
j=i+len-1;
if(a[i]==a[j]){
for(int x=c[j];x<=1000;++x)
chkmax(g[i][j][x],g[i][j-1][x-c[j]]);
}
for(int p=i;p<j;++p){
for(int x=1;x<=1000;++x)
chkmax(g[i][j][x],g[i][p][x]+f[p+1][j]);
}
for(int p=i;p<j;++p)
chkmax(f[i][j],f[i][p]+f[p+1][j]);
for(int x=1;x<=1000;++x)
chkmax(f[i][j],g[i][j][x]+x*x);
}
}
cout<<f[1][n];
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
TEST_152
从左往右扫描线一遍操作序列,ODT 维护每一段最后一次操作的 id 和值,查询查 id 大于等于左端点的序列和即可。复杂度 \(O(n\log n)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=5e5+9,M=1000+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
#define int long long
int n,m,q,a[N],ql[N],qr[N],qv[N],ans[N];
struct BIT{
int f[N];
void modify(int u,int x){
if(!u) return ;
for(;u<=n;u+=lowbit(u))
f[u]+=x;
}
int query(int u){
int ret=0;
for(;u;u-=lowbit(u))
ret+=f[u];
return ret;
}
} T;
struct ask{
int l,r,id;
bool operator<(const ask &res)const{
return r<res.r;
}
} h[N];
struct rg{
mutable int l,r,v,t;
bool operator<(const rg &res)const{
return l<res.l;
}
};
set<rg> s;
auto split(int pos){
auto it=s.lower_bound({pos,0,0,0});
if(it->l==pos) return it;
--it; int l=it->l,r=it->r,v=it->v,t=it->t;
s.erase(it); s.insert({l,pos-1,v,t});
return s.insert({pos,r,v,t}).fir;
}
void assign(int L,int R,int v,int t){
auto itr=split(R+1); auto itl=split(L);
for(auto it=itl;it!=itr;++it)
T.modify(it->t,-1ll*((it->r)-(it->l)+1)*it->v);
s.erase(itl,itr);
T.modify(t,(R-L+1)*v); s.insert({L,R,v,t});
}
void Mian(){
cin>>n>>m>>q;
for(int i=1;i<=n;++i) cin>>ql[i]>>qr[i]>>qv[i];
for(int i=1;i<=q;++i){
cin>>h[i].l>>h[i].r;
h[i].id=i;
}
sort(h+1,h+q+1);
s.insert({0,m+1,0,0});
for(int i=1,j=1;i<=n;++i){
assign(ql[i],qr[i],qv[i],i);
while(j<=q && h[j].r==i){
ans[h[j].id]=T.query(n)-T.query(h[j].l-1);
++j;
}
}
for(int i=1;i<=q;++i) cout<<ans[i]<<'\n';
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("P10544_3.in","r",stdin);
//freopen("P10544_3.op","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
镜中的昆虫
神题。
先考虑单点修改怎么做,因为每次修改对 \(pre\) 数组影响是 \(O(1)\) 的,所以加一个时间维作三维偏序即可。
现在是区间推平,由颜色段均摊,所有修改对 \(pre\) 数组影响是 \(O(n+m)\) 的,那么跟单点一样暴力改就行了。
尤其注意 odt 的细节处理,以及空间(64MB)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=1e5+9,M=2e5+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,m,ans[N],lt,tot,qcnt,lst[N];
map<int,int> mp;
struct BIT{
int f[N];
void modify(int u,int x){
for(;u<=n;u+=lowbit(u))
f[u]+=x;
}
int query(int u){
int ret=0;
for(;u;u-=lowbit(u))
ret+=f[u];
return ret;
}
} T;
struct ask{
int op,x,y,z,coef,id;
} q[1020892],tmp[1020892];
bool cmp1(ask u,ask v){
return u.x!=v.x?u.x<v.x:u.op<v.op;
}
bool cmp2(ask u,ask v){
return u.y!=v.y?u.y<v.y:u.op<v.op;
}
void cdq(int l,int r){
if(l==r) return ;
int mid=l+r>>1;
cdq(l,mid); cdq(mid+1,r);
for(int i=mid+1,j=l;i<=r;++i){
while(j<=mid && q[j].y<=q[i].y){
if(!q[j].op) T.modify(q[j].z,q[j].coef);
++j;
}
if(q[i].op) ans[q[i].id]+=q[i].coef*T.query(q[i].z);
}
for(int i=mid+1,j=l;i<=r;++i){
while(j<=mid && q[j].y<=q[i].y){
if(!q[j].op) T.modify(q[j].z,-q[j].coef);
++j;
}
}
int i=l,j=mid+1,k=l-1;
while(i<=mid && j<=r){
if(cmp2(q[i],q[j])) tmp[++k]=q[i++];
else tmp[++k]=q[j++];
}
while(i<=mid) tmp[++k]=q[i++];
while(j<=r) tmp[++k]=q[j++];
for(int o=l;o<=r;++o) q[o]=tmp[o];
}
struct rg{
mutable int l,r,v;
bool operator<(const rg &res)const{
return l<res.l;
}
};
set<rg> s,col[M];
auto ins(int l,int r,int v){
col[v].insert({l,r,v});
return s.insert({l,r,v}).fir;
}
void del(int l,int r,int v){
col[v].erase({l,r,v});
s.erase({l,r,v});
}
auto split(int pos){
auto it=s.lower_bound({pos,0,0});
if(it->l==pos) return it;
--it; int l=it->l,r=it->r,v=it->v;
del(l,r,v); ins(l,pos-1,v);
return ins(pos,r,v);
}
int getpre(int pos){
auto it=--s.upper_bound({pos,0,0});
if(it->l<pos) return pos-1;
auto it2=col[it->v].lower_bound({it->l,0,0});
if(it2!=col[it->v].begin()) return (--it2)->r;
return 0;
}
void assign(int L,int R,int v,int t){
auto itr=split(R+1),itl=split(L);
vector<int> tmp; vector<rg> dmp;
for(auto it=itl;it!=itr;++it){
tmp.pb(it->l);
auto it2=col[it->v].upper_bound({it->l,0,0});
if(it2!=col[it->v].end()) tmp.pb(it2->l);
dmp.pb({it->l,it->r,it->v});
}
auto itp=col[v].upper_bound({R,0,0});
if(itp!=col[v].end()) tmp.pb(itp->l);
sort(tmp.begin(),tmp.end());
tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
for(auto u:tmp) q[++tot]={0,t,getpre(u),u,-1,0};
for(auto i:dmp) del(i.l,i.r,i.v);
ins(L,R,v);
for(auto u:tmp) q[++tot]={0,t,getpre(u),u,1,0};
}
void Mian(){
cin>>n>>m;
for(int i=1;i<=n;++i){
int x; cin>>x;
if(!mp[x]) mp[x]=++lt;
x=mp[x];
q[++tot]={0,0,lst[x],i,1,0};
ins(i,i,x);
lst[x]=i;
}
s.insert({0,0,0}); s.insert({n+1,n+1,0});
for(int i=1;i<=m;++i){
int op; cin>>op;
if(op==1){
int l,r,x; cin>>l>>r>>x;
if(!mp[x]) mp[x]=++lt;
x=mp[x];
assign(l,r,x,i);
}
if(op==2){
int l,r; cin>>l>>r;
q[++tot]={1,i,l-1,l-1,-1,++qcnt};
q[++tot]={1,i,l-1,r,1,qcnt};
}
}
sort(q+1,q+tot+1,cmp1);
cdq(1,tot);
for(int i=1;i<=qcnt;++i) cout<<ans[i]<<'\n';
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
TEST_95
本质不同其实是值不同,那么直接对每种值 \(i\) 维护 \(F_i,G_i\) 表示第一次和最后一次出现的位置,这样不带修的求法就是二维偏序,加入修改后添加时间维作三维偏序即可。
注意重复计算,枚举顺序等各种细节。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=1e5+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,m,a[N],tot,lt;
i64 ans[N];
struct BIT{
int f[N];
void modify(int u,int val){
for(;u<=m+1;u+=lowbit(u))
f[u]+=val;
}
int query(int u){
int ret=0;
for(;u;u-=lowbit(u))
ret+=f[u];
return ret;
}
int query(int L,int R){
if(L>R) return 0;
return query(R)-query(L-1);
}
} T;
int F[N],G[N];
set<int> s[N];
struct node{
int op,x,y,z,coef;
} q[M],tmp[M];
bool cmp1(node u,node v){
return u.x!=v.x?u.x<v.x:u.op<v.op;
}
bool cmp2(node u,node v){
return u.y!=v.y?u.y<v.y:u.op<v.op;
}
void cdq(int l,int r){
if(l==r) return ;
int mid=l+r>>1;
cdq(l,mid); cdq(mid+1,r);
for(int i=r,j=mid;i>=mid+1;--i){
while(j>=l && q[j].y>q[i].y){
if(q[j].op) T.modify(q[j].z,q[j].coef);
--j;
}
if(!q[i].op) ans[q[i].z]+=q[i].coef*T.query(q[i].z);
}
for(int i=r,j=mid;i>=mid+1;--i){
while(j>=l && q[j].y>q[i].y){
if(q[j].op) T.modify(q[j].z,-q[j].coef);
--j;
}
}
for(int i=l,j=mid+1;i<=mid;++i){
while(j<=r && q[j].y<q[i].y){
if(!q[j].op) T.modify(q[j].z,q[j].coef);
++j;
}
if(q[i].op) ans[q[i].z]+=q[i].coef*T.query(q[i].z);
}
for(int i=l,j=mid+1;i<=mid;++i){
while(j<=r && q[j].y<q[i].y){
if(!q[j].op) T.modify(q[j].z,-q[j].coef);
++j;
}
}
int i=l,j=mid+1,k=l-1;
while(i<=mid && j<=r){
if(cmp2(q[i],q[j])) tmp[++k]=q[i++];
else tmp[++k]=q[j++];
}
while(i<=mid) tmp[++k]=q[i++];
while(j<=r) tmp[++k]=q[j++];
for(int o=l;o<=r;++o) q[o]=tmp[o];
}
void upd(int col,int t){
int L,R;
if(s[col].empty()) L=n+1,R=0;
else L=*s[col].begin(),R=*s[col].rbegin();
if(L!=F[col]){
q[++tot]={0,col,F[col],t,-1};
q[++tot]={0,col,F[col]=L,t,1};
}
if(R!=G[col]){
q[++tot]={1,col,G[col],t,-1};
q[++tot]={1,col,G[col]=R,t,1};
}
}
void Mian(){
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
s[a[i]].insert(i);
}
for(int i=1;i<=n;++i){
if(s[i].empty()) F[i]=n+1,G[i]=0;
else F[i]=*s[i].begin(),G[i]=*s[i].rbegin();
q[++tot]={0,i,F[i],1,1};
q[++tot]={1,i,G[i],1,1};
}
cin>>m;
for(int i=1;i<=m;++i){
int x,y; cin>>x>>y;
s[a[x]].erase(x);
upd(a[x],i+1);
s[y].insert(x); a[x]=y;
upd(y,i+1);
}
sort(q+1,q+tot+1,cmp1);
cdq(1,tot);
for(int i=1;i<=m+1;++i){
if(i>1) ans[i]+=ans[i-1];
else ans[i]/=2;
cout<<ans[i]<<'\n';
}
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
异或运算
\(n\) 很小,对 \(Y\) 数组建可持久化 trie,对 \(X\) 直接暴力,每次以 \(O(n)\) 的代价确定答案的一个二进制位,总复杂度 \(O(qn\log V)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=3e5+9,M=1e7+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,m,q,a[N],b[M],th[N][2];
int root[N],ch[M][2],tot,siz[M];
int copy(int u){
++tot;
ch[tot][0]=ch[u][0]; ch[tot][1]=ch[u][1];
siz[tot]=siz[u];
return tot;
}
int ins(int u,int val){
int now=copy(u),tmp=now;
for(int j=31;j>=0;--j){
int t=((val>>j)&1);
now=ch[now][t]=copy(ch[u][t]);
u=ch[u][t];
siz[now]++;
}
return tmp;
}
void Mian(){
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=m;++i) cin>>b[i];
for(int i=1;i<=m;++i) root[i]=ins(root[i-1],b[i]);
cin>>q;
while(q--){
int l1,r1,l2,r2,k; cin>>l1>>r1>>l2>>r2>>k;
i64 ans=0;
for(int i=l1;i<=r1;++i){
th[i][0]=root[l2-1];
th[i][1]=root[r2];
}
for(int j=31;j>=0;--j){
int cnt=0;
for(int i=l1;i<=r1;++i){
int t=(a[i]>>j)&1;
cnt+=(siz[ch[th[i][1]][t^1]]-siz[ch[th[i][0]][t^1]]);
}
if(k<=cnt){
ans|=(1<<j);
for(int i=l1;i<=r1;++i){
int t=(a[i]>>j)&1;
th[i][0]=ch[th[i][0]][t^1];
th[i][1]=ch[th[i][1]][t^1];
}
}else{
k-=cnt;
for(int i=l1;i<=r1;++i){
int t=(a[i]>>j)&1;
th[i][0]=ch[th[i][0]][t];
th[i][1]=ch[th[i][1]][t];
}
}
}
cout<<ans<<'\n';
}
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
middle
二分答案,把大于等于 mid 的数看成 1,小于 mid 看成 -1,则 mid 合法等价于当前区间的和大于等于 0。预处理每种可能的答案对应的线段树,由于每次答案变大时只会有一个点从 1 变成 -1,主席树即可。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=20000+9,M=1e7+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,q,a[N],id[N],root[N],tot;
bool cmp(int x,int y){
return a[x]<a[y];
}
struct info{
int lmx,rmx,sum;
};
info merge(info L,info R){
info ret;
ret.sum=L.sum+R.sum;
ret.lmx=max(L.lmx,L.sum+R.lmx);
ret.rmx=max(R.rmx,R.sum+L.rmx);
return ret;
}
struct node{
int ls,rs;
info t;
} tre[N*60];
int copy(int u){
++tot; tre[tot]=tre[u];
return tot;
}
int build(int l,int r){
int u=++tot;
if(l==r){
tre[u].ls=tre[u].rs=0;
tre[u].t={1,1,1};
return tot;
}
int mid=l+r>>1;
tre[u].ls=build(l,mid);
tre[u].rs=build(mid+1,r);
tre[u].t=merge(tre[tre[u].ls].t,tre[tre[u].rs].t);
return u;
}
int modify(int u,int l,int r,int x){
u=copy(u);
if(l==r){
tre[u].t={-1,-1,-1};
return u;
}
int mid=l+r>>1;
if(x<=mid) tre[u].ls=modify(tre[u].ls,l,mid,x);
if(x>mid) tre[u].rs=modify(tre[u].rs,mid+1,r,x);
tre[u].t=merge(tre[tre[u].ls].t,tre[tre[u].rs].t);
return u;
}
info query(int u,int l,int r,int L,int R){
if(L<=l && r<=R) return tre[u].t;
int mid=l+r>>1;
if(R<=mid) return query(tre[u].ls,l,mid,L,R);
if(L>mid) return query(tre[u].rs,mid+1,r,L,R);
return merge(query(tre[u].ls,l,mid,L,R),query(tre[u].rs,mid+1,r,L,R));
}
bool check(int v,int l1,int r1,int l2,int r2){
int sum=query(root[v],1,n,l1,r1).rmx+query(root[v],1,n,l2,r2).lmx;
if(r1<l2-1) sum+=query(root[v],1,n,r1+1,l2-1).sum;
return sum>=0;
}
void Mian(){
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i) id[i]=i;
sort(id+1,id+n+1,cmp);
tot=0;
root[1]=build(1,n);
for(int i=2;i<=n;++i)
root[i]=modify(root[i-1],1,n,id[i-1]);
cin>>q;
int lstans=0;
while(q--){
int tmp[4]; for(int i=0;i<4;++i) cin>>tmp[i];
for(int i=0;i<4;++i) (tmp[i]+=lstans)%=n;
sort(tmp,tmp+4);
int l1=tmp[0]+1,r1=tmp[1]+1,l2=tmp[2]+1,r2=tmp[3]+1;
int L=1,R=n,res;
while(L<=R){
int mid=L+R>>1;
if(check(mid,l1,r1,l2,r2)) L=(res=mid)+1;
else R=mid-1;
}
cout<<(lstans=a[id[res]])<<'\n';
}
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
The Classic Problem
神题啊。
首先我们肯定想做 Dijkstra,这就需要支持大数的比较和形如 \(dis[y]=dis[x]+w\) 的更新。我们可以使用一些数据结构维护这个非常大的数,更新相当于把原来的数复制过来一份,然后做修改,启发我们使用可持久化。由于做加法的边权都是 \(2^x\) 的形式,所以我们用线段树维护这些大数的每一个二进制位,加上边权时相当于查询从某个位置开始的极长 \(1\) 连续段,区间推平为 \(0\) 和一个单点修改为 \(1\),这些在线段树上都是不难的。
总复杂度 \(O(n\log^2 n)\)。
尤其注意数组不要开小了,\(x \leq 10^5\) 不代表你的距离范围也 \(\leq 2^{10^5}\)。。。
相信你也不想:

upd:
在具体实现上有两种方法,一种是经典的直接打区间推平的 tag,但是这里我们维护的是可持久化线段树,于是每次 pushdown 都需要新建节点,笔者这里为了防止重复新建浪费太多空间,还再打了一个孩子是否是新建节点的 tag,空间占用较大。
另一种是对于那些区间推平的操作直接把对应节点修改成 空节点 \(0\),每次 modify 进来的时候又会新建节点,空间占用相对较小,且也是正确的。
AC Code (ver. 1)
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=1e5+109,M=2e7+9;
const int MOD=1e9+7;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,m,up=(int)(1e5+102),s,t,root[N],tot,tmp;
int bs0=2,bs1=251,pw0[N],pw1[N];
struct node{
int ls,rs;
int sum,hs0,hs1;
int tag,dn;
} tre[M];
void push_up(int u,int l,int r){
int mid=l+r>>1;
tre[u].sum=tre[tre[u].ls].sum+tre[tre[u].rs].sum;
tre[u].hs0=(tre[tre[u].ls].hs0+tre[tre[u].rs].hs0)%MOD;
tre[u].hs1=(tre[tre[u].ls].hs1+tre[tre[u].rs].hs1)%MOD;
}
int build(int l,int r,int x){
int u=++tot;
if(l==r){
tre[u].sum=x;
tre[u].hs0=pw0[l]*x;
tre[u].hs1=pw1[l]*x;
return u;
}
int mid=l+r>>1;
tre[u].ls=build(l,mid,x);
tre[u].rs=build(mid+1,r,x);
push_up(u,l,r);
return u;
}
void push_down(int u,int l,int r){
if(!tre[u].dn){
tre[++tot]=tre[tre[u].ls]; tre[u].ls=tot;
tre[++tot]=tre[tre[u].rs]; tre[u].rs=tot;
tre[u].dn=1;
}
if(!tre[u].tag) return ;
tre[tre[u].ls].sum=tre[tre[u].rs].sum=0;
tre[tre[u].ls].hs0=tre[tre[u].rs].hs0=0;
tre[tre[u].ls].hs1=tre[tre[u].rs].hs1=0;
tre[tre[u].ls].dn=tre[tre[u].rs].dn=0;
tre[tre[u].ls].tag=tre[tre[u].rs].tag=1;
tre[u].tag=0;
}
void modify(int &u,int l,int r,int x){
tre[++tot]=tre[u]; u=tot;
tre[u].dn=0;
if(l==r)
return tre[u].sum=1,tre[u].hs0=pw0[l],tre[u].hs1=pw1[l],void();
push_down(u,l,r);
int mid=l+r>>1;
if(x<=mid) modify(tre[u].ls,l,mid,x);
if(x>mid) modify(tre[u].rs,mid+1,r,x);
push_up(u,l,r);
}
void change(int &u,int l,int r,int L,int R){
tre[++tot]=tre[u]; u=tot;
tre[u].dn=0;
if(L<=l && r<=R){
tre[u].sum=tre[u].hs0=tre[u].hs1=0;
tre[u].tag=1;
return ;
}
int mid=l+r>>1;
push_down(u,l,r);
if(L<=mid) change(tre[u].ls,l,mid,L,R);
if(R>mid) change(tre[u].rs,mid+1,r,L,R);
push_up(u,l,r);
}
int query(int u,int l,int r,int L,int R){
if(L<=l && r<=R) return tre[u].sum;
int mid=l+r>>1,ret=0;
push_down(u,l,r);
if(L<=mid) ret+=query(tre[u].ls,l,mid,L,R);
if(R>mid) ret+=query(tre[u].rs,mid+1,r,L,R);
return ret;
}
int get(int u,int l,int r,int x){
if(l==r) return tre[u].sum;
push_down(u,l,r);
int mid=l+r>>1;
if(x>mid) return get(tre[u].rs,mid+1,r,x);
if(query(tre[u].ls,l,mid,x,mid)==mid-x+1)
return mid-x+1+get(tre[u].rs,mid+1,r,mid+1);
return get(tre[u].ls,l,mid,x);
}
int add(int u,int x){
int len=get(u,0,up,x);
modify(u,0,up,x+len);
if(len) change(u,0,up,x,x+len-1);
return u;
}
bool chk(int x,int y){
return tre[x].hs0==tre[y].hs0 && tre[x].hs1==tre[y].hs1;
}
bool cmp(int x,int y,int l,int r){
if(l==r) return tre[x].sum<=tre[y].sum;
push_down(x,l,r); push_down(y,l,r);
int mid=l+r>>1;
if(chk(tre[x].rs,tre[y].rs))
return cmp(tre[x].ls,tre[y].ls,l,mid);
else return cmp(tre[x].rs,tre[y].rs,mid+1,r);
}
struct LH{
int ls[N<<2],rs[N<<2];
int pu[N<<2],rt[N<<2];
int dist[N<<2],idx,root,siz;
int merge(int x,int y){
if(!x || !y) return (x|y);
if(cmp(rt[y],rt[x],0,up)) swap(x,y);
rs[x]=merge(rs[x],y);
if(dist[rs[x]]>dist[ls[x]]) swap(ls[x],rs[x]);
dist[x]=dist[ls[x]]+1;
return x;
}
void push(int iu,int irt){
++siz;
pu[++idx]=iu,rt[idx]=irt;
root=merge(root,idx);
}
int top(){return pu[root];}
void pop(){--siz; root=merge(ls[root],rs[root]);}
bool empty(){return (siz==0);}
} q;
bool vis[N];
int fr[N],st[N],tp;
vector<pii> G[N];
void Dijkstra(){
int mx=build(0,up,1),mi=build(0,up,0);
for(int i=1;i<=n;++i) root[i]=mx;
root[s]=mi;
q.push(s,root[s]);
while(!q.empty()){
int u=q.top(); q.pop();
if(vis[u]) continue;
vis[u]=1;
for(auto y:G[u]){
int v=y.fir,w=y.sec;
if(vis[v]) continue;
int tmp=add(root[u],w);
if(!cmp(root[v],tmp,0,up)){
root[v]=tmp; fr[v]=u;
q.push(v,root[v]);
}
}
}
if(!vis[t]) return cout<<-1,void();
cout<<tre[root[t]].hs0<<'\n';
for(int i=t;i;i=fr[i]) st[++tp]=i;
cout<<tp<<'\n';
while(tp){
cout<<st[tp]<<' ';
--tp;
}
}
void Mian(){
pw0[0]=pw1[0]=1;
for(int i=1;i<=up;++i)
pw0[i]=1ll*pw0[i-1]*bs0%MOD,pw1[i]=1ll*pw1[i-1]*bs1%MOD;
cin>>n>>m;
for(int i=1;i<=m;++i){
int u,v,x; cin>>u>>v>>x;
G[u].pb({v,x});
G[v].pb({u,x});
} cin>>s>>t;
Dijkstra();
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
AC Code (ver. 2)
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=1e5+109,M=5e6+9;
const int MOD=1e9+7;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,m,up=(int)(1e5+100),s,t,root[N],tot,tmp;
int bs0=2,bs1=251,pw0[N],pw1[N];
struct node{
int ls,rs;
int sum,hs0,hs1;
} tre[M];
void push_up(int u,int l,int r){
int mid=l+r>>1;
tre[u].sum=tre[tre[u].ls].sum+tre[tre[u].rs].sum;
tre[u].hs0=(tre[tre[u].ls].hs0+tre[tre[u].rs].hs0)%MOD;
tre[u].hs1=(tre[tre[u].ls].hs1+tre[tre[u].rs].hs1)%MOD;
}
int build(int l,int r,int x){
int u=++tot;
if(l==r){
tre[u].sum=x;
tre[u].hs0=pw0[l]*x;
tre[u].hs1=pw1[l]*x;
return u;
}
int mid=l+r>>1;
tre[u].ls=build(l,mid,x);
tre[u].rs=build(mid+1,r,x);
push_up(u,l,r);
return u;
}
void modify(int &u,int l,int r,int x){
tre[++tot]=tre[u]; u=tot;
if(l==r)
return tre[u].sum=1,tre[u].hs0=pw0[l],tre[u].hs1=pw1[l],void();
int mid=l+r>>1;
if(x<=mid) modify(tre[u].ls,l,mid,x);
if(x>mid) modify(tre[u].rs,mid+1,r,x);
push_up(u,l,r);
}
void change(int &u,int l,int r,int L,int R){
if(L<=l && r<=R) return u=0,void();
tre[++tot]=tre[u]; u=tot;
int mid=l+r>>1;
if(L<=mid) change(tre[u].ls,l,mid,L,R);
if(R>mid) change(tre[u].rs,mid+1,r,L,R);
push_up(u,l,r);
}
int query(int u,int l,int r,int L,int R){
if(L<=l && r<=R) return tre[u].sum;
int mid=l+r>>1,ret=0;
if(L<=mid) ret+=query(tre[u].ls,l,mid,L,R);
if(R>mid) ret+=query(tre[u].rs,mid+1,r,L,R);
return ret;
}
int get(int u,int l,int r,int x){
if(l==r) return tre[u].sum;
int mid=l+r>>1;
if(x>mid) return get(tre[u].rs,mid+1,r,x);
if(query(tre[u].ls,l,mid,x,mid)==mid-x+1)
return mid-x+1+get(tre[u].rs,mid+1,r,mid+1);
return get(tre[u].ls,l,mid,x);
}
int add(int u,int x){
int len=get(u,0,up,x);
modify(u,0,up,x+len);
if(len) change(u,0,up,x,x+len-1);
return u;
}
bool chk(int x,int y){
return tre[x].hs0==tre[y].hs0 && tre[x].hs1==tre[y].hs1;
}
bool cmp(int x,int y,int l,int r){
if(l==r) return tre[x].sum<=tre[y].sum;
int mid=l+r>>1;
if(chk(tre[x].rs,tre[y].rs))
return cmp(tre[x].ls,tre[y].ls,l,mid);
else return cmp(tre[x].rs,tre[y].rs,mid+1,r);
}
struct LH{
int ls[N<<2],rs[N<<2];
int pu[N<<2],rt[N<<2];
int dist[N<<2],idx,root,siz;
int merge(int x,int y){
if(!x || !y) return (x|y);
if(cmp(rt[y],rt[x],0,up)) swap(x,y);
rs[x]=merge(rs[x],y);
if(dist[rs[x]]>dist[ls[x]]) swap(ls[x],rs[x]);
dist[x]=dist[ls[x]]+1;
return x;
}
void push(int iu,int irt){
++siz;
pu[++idx]=iu,rt[idx]=irt;
root=merge(root,idx);
}
int top(){return pu[root];}
void pop(){--siz; root=merge(ls[root],rs[root]);}
bool empty(){return (siz==0);}
} q;
bool vis[N];
int fr[N],st[N],tp;
vector<pii> G[N];
void Dijkstra(){
int mx=build(0,up,1),mi=build(0,up,0);
for(int i=1;i<=n;++i) root[i]=mx;
root[s]=mi;
q.push(s,root[s]);
while(!q.empty()){
int u=q.top(); q.pop();
if(vis[u]) continue;
vis[u]=1;
for(auto y:G[u]){
int v=y.fir,w=y.sec;
if(vis[v]) continue;
int tmp=add(root[u],w);
if(!cmp(root[v],tmp,0,up)){
root[v]=tmp; fr[v]=u;
q.push(v,root[v]);
}
}
}
if(!vis[t]) return cout<<-1,void();
cout<<tre[root[t]].hs0<<'\n';
for(int i=t;i;i=fr[i]) st[++tp]=i;
cout<<tp<<'\n';
while(tp){
cout<<st[tp]<<' ';
--tp;
}
}
void Mian(){
pw0[0]=pw1[0]=1;
for(int i=1;i<=up;++i)
pw0[i]=1ll*pw0[i-1]*bs0%MOD,pw1[i]=1ll*pw1[i-1]*bs1%MOD;
cin>>n>>m;
for(int i=1;i<=m;++i){
int u,v,x; cin>>u>>v>>x;
G[u].pb({v,x});
G[v].pb({u,x});
} cin>>s>>t;
Dijkstra();
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>T;
while(T--){
Mian();
Mianclr();
}
}
森林
链 \(k\) 小问题一般在树上建主席树,这里显然没有什么优化空间。那么我们连边的时候就必定要重构两个连通块之一的所有信息,这启发我们使用启发式合并。这样每个点至多被重构 \(\log n\) 次,总复杂度 \(O(n\log^2 n)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=8e4+9,M=5e6+9;
const int MOD=1e9+7;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,m,q,a[N],b[N],up,tot,root[N];
struct node{
int ls,rs,sum;
} tre[N*400];
void push_up(int u){
tre[u].sum=tre[tre[u].ls].sum+tre[tre[u].rs].sum;
}
int build(int l,int r){
int u=++tot;
if(l==r){
tre[u].sum=0;
return u;
}
int mid=l+r>>1;
tre[u].ls=build(l,mid);
tre[u].rs=build(mid+1,r);
push_up(u);
return u;
}
void modify(int &u,int l,int r,int x){
tre[++tot]=tre[u]; u=tot;
if(l==r) return tre[u].sum++,void();
int mid=l+r>>1;
if(x<=mid) modify(tre[u].ls,l,mid,x);
if(x>mid) modify(tre[u].rs,mid+1,r,x);
push_up(u);
}
int query(int a1,int a2,int b1,int b2,int l,int r,int k){
if(l==r) return l;
int tmp=tre[tre[a1].ls].sum+tre[tre[a2].ls].sum
-tre[tre[b1].ls].sum-tre[tre[b2].ls].sum,mid=l+r>>1;
if(k<=tmp)
return query(tre[a1].ls,tre[a2].ls,tre[b1].ls,tre[b2].ls,l,mid,k);
return query(tre[a1].rs,tre[a2].rs,tre[b1].rs,tre[b2].rs,mid+1,r,k-tmp);
}
int fa[N],siz[N],dep[N],st[N][29];
bool vis[N];
vector<int> G[N];
int find(int u){
return fa[u]==u?u:fa[u]=find(fa[u]);
}
void dfs(int u,int ft){
vis[u]=1;
st[u][0]=ft; fa[u]=ft; siz[u]=1;
for(int j=1;j<=19;++j) st[u][j]=st[st[u][j-1]][j-1];
dep[u]=dep[ft]+1;
root[u]=root[ft];
modify(root[u],1,up,a[u]);
for(auto v:G[u]){
if(v==ft) continue;
dfs(v,u); siz[u]+=siz[v];
}
}
int lca(int u,int v){
if(dep[u]>dep[v]) swap(u,v);
for(int j=19;j>=0;--j)
if(dep[st[v][j]]>=dep[u])
v=st[v][j];
if(u==v) return u;
for(int j=19;j>=0;--j)
if(st[u][j]!=st[v][j])
u=st[u][j],v=st[v][j];
return st[u][0];
}
void Mian(){
cin>>n>>m>>q;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i) b[i]=a[i];
sort(b+1,b+n+1);
up=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i)
a[i]=lower_bound(b+1,b+up+1,a[i])-b;
root[0]=build(1,up);
for(int i=1;i<=m;++i){
int u,v; cin>>u>>v;
G[u].pb(v);
G[v].pb(u);
}
for(int i=1;i<=n;++i) if(!vis[i]){
dfs(i,0);
fa[i]=i;
}
int lstans=0;
while(q--){
char opt; cin>>opt;
if(opt=='Q'){
int x,y,k; cin>>x>>y>>k;
x^=lstans,y^=lstans,k^=lstans;
int fu=lca(x,y);
cout<<(lstans=b[query(root[x],root[y],root[fu],root[st[fu][0]],1,up,k)])<<'\n';
}
if(opt=='L'){
int x,y; cin>>x>>y;
x^=lstans,y^=lstans;
G[x].pb(y); G[y].pb(x);
int gx=find(x),gy=find(y);
if(siz[gx]>siz[gy]) swap(x,y),swap(gx,gy);
dfs(x,y); siz[y]+=siz[x];
}
}
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; cin>>c;
while(T--){
Mian();
Mianclr();
}
}
Unknown
二进制分组,固定一个向量的叉乘可以写成一个直线的形式,所以对于每一个块维护一个上凸包,二分用直线去切这个凸包就能做到单块 \(O(\log n)\) 查询。
但带删的话由于合并的复杂度是均摊的,可以被卡爆。这里一个很厉害的想法是当我们有多个长度为 \(2^k\) 的区间的时候再去合并其中两个为 \(2^{k+1}\) 长的区间,即总是保留一些长度更小的区间,这样想要对一个 \(2^k\) 的区间有影响(使其需要重构)的话,也至少需要 \(O(2^k)\) 次删除操作,时间复杂度就是正确的。
写成线段树形式的话注意一下最左侧一列是没有前继节点的,总复杂度两只 \(\log\)。
但是有人交 hack 把我空间卡爆了,呜呜。
并没有 AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (2e18)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=3e5+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,m,up,ans,si;
struct ver{
int x,y;
inline ver operator-(ver res)const{
return {x-res.x,y-res.y};
}
inline i64 operator*(ver res)const{
return (i64)x*res.y-(i64)res.x*y;
}
};
bitset<(1<<20|1)> dn;
vector<ver> v[1<<20|1],tmp;
i64 get(int u,ver x){
int L=0,R=v[u].size(),res=0;
while(L+1<R){
int mid=L+R>>1;
if(x*v[u][mid]>=x*v[u][mid-1]) L=(res=mid);
else R=mid;
}
return x*v[u][res];
}
void merge(int u,int ls,int rs){
v[u].clear(); tmp.clear();
int i=0,j=0;
while(i<v[ls].size() && j<v[rs].size()){
if(v[ls][i].x<v[rs][j].x) tmp.pb(v[ls][i++]);
else if(v[ls][i].x>v[rs][j].x) tmp.pb(v[rs][j++]);
else{
if(v[ls][i].y>=v[rs][j].y) tmp.pb(v[ls][i]);
else tmp.pb(v[rs][j]);
++i; ++j;
}
}
while(i<v[ls].size()) tmp.pb(v[ls][i++]);
while(j<v[rs].size()) tmp.pb(v[rs][j++]);
for(auto i:tmp){
while(v[u].size()>=2 &&
(i-v[u].back())*(v[u].back()-v[u][v[u].size()-2])<=0ll) v[u].pop_back();
v[u].pb(i);
}
}
void ins(int u,int l,int r,int pos,ver x){
if(l==r){
chkmax(si,u);
v[u].pb(x); dn[u]=1;
return ;
}
int mid=l+r>>1;
if(pos<=mid) ins(u<<1,l,mid,pos,x);
if(pos>mid) ins(u<<1|1,mid+1,r,pos,x);
if(u!=lowbit(u) && pos==r && !dn[u-1])
merge(u-1,(u-1)<<1,(u-1)<<1|1),dn[u-1]=1;
}
void del(int u,int l,int r,int pos){
dn[u]=0; v[u].clear();
if(l==r){
chkmax(si,u);
return ;
}
int mid=l+r>>1;
if(pos<=mid) del(u<<1,l,mid,pos);
if(pos>mid) del(u<<1|1,mid+1,r,pos);
}
i64 qry(int u,int l,int r,int L,int R,ver x){
if(L<=l && r<=R && dn[u]) return get(u,x);
if(l==r) return -INF;
int mid=l+r>>1; i64 ret=-INF;
if(L<=mid) chkmax(ret,qry(u<<1,l,mid,L,R,x));
if(R>mid) chkmax(ret,qry(u<<1|1,mid+1,r,L,R,x));
return ret;
}
void Mian(){
up=(1<<19);
while(cin>>m && m){
dn.reset();
for(int i=1;i<=si;++i) v[i].clear();
si=ans=n=0;
for(int i=1;i<=m;++i){
int opt; cin>>opt;
if(opt==1){
int x,y; cin>>x>>y;
ins(1,1,up,++n,{x,y});
}
if(opt==2) del(1,1,up,n),--n;
if(opt==3){
int l,r,x,y; cin>>l>>r>>x>>y;
ans^=((qry(1,1,up,l,r,{x,y})%MOD+MOD)%MOD);
}
} cout<<ans<<'\n';
}
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; cin>>c;
while(T--){
Mian();
Mianclr();
}
}
德莉莎世界第一可爱
四维偏序。重复元素钦定一个顺序即可,复杂度 \(O(n\log^3 n)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define i28 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e18)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=50000+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
struct BIT{
ll n,f[N];
void init(){for(int i=1;i<=n;++i) f[i]=-INF;}
void modify(int u,ll val){
for(;u<=n;u+=lowbit(u))
chkmax(f[u],val);
}
ll query(int u){
ll ret=-INF;
for(;u;u-=lowbit(u))
chkmax(ret,f[u]);
return ret;
}
void clr(int u){
for(;u<=n;u+=lowbit(u))
f[u]=-INF;
}
} T;
int n,h[N],tot;
ll dp[N];
struct node{
int t,x,y,z;
int tag,id,w;
} a[N],b[N],c[N];
bool cmp1(node u,node v){
if(u.t!=v.t) return u.t<v.t;
if(u.x!=v.x) return u.x<v.x;
if(u.y!=v.y) return u.y<v.y;
if(u.z!=v.z) return u.z<v.z;
return u.id<v.id;
}
bool cmp2(node u,node v){
if(u.x!=v.x) return u.x<v.x;
if(u.y!=v.y) return u.y<v.y;
if(u.z!=v.z) return u.z<v.z;
if(u.tag!=v.tag) return u.tag<v.tag;
return u.id<v.id;
}
bool cmp3(node u,node v){
if(u.y!=v.y) return u.y<v.y;
if(u.z!=v.z) return u.z<v.z;
if(u.tag!=v.tag) return u.tag<v.tag;
if(u.x!=v.x) return u.x<v.x;
return u.id<v.id;
}
void cdq2(int l,int r){
if(l==r) return ;
int mid=l+r>>1;
cdq2(l,mid);
for(int i=l;i<=r;++i) c[i]=b[i];
sort(c+l,c+mid+1,cmp3); sort(c+mid+1,c+r+1,cmp3);
for(int i=mid+1,j=l;i<=r;++i){
while(j<=mid && c[j].y<=c[i].y){
if(!c[j].tag) T.modify(c[j].z,dp[c[j].id]);
++j;
}
if(c[i].tag) chkmax(dp[c[i].id],T.query(c[i].z)+c[i].w);
}
for(int i=mid+1,j=l;i<=r;++i){
while(j<=mid && c[j].y<=c[i].y){
if(!c[j].tag) T.clr(c[j].z);
++j;
}
}
cdq2(mid+1,r);
}
void cdq1(int l,int r){
if(l==r) return ;
int mid=l+r>>1;
cdq1(l,mid);
for(int i=l;i<=r;++i) b[i]=a[i];
for(int i=l;i<=mid;++i) b[i].tag=0;
for(int i=mid+1;i<=r;++i) b[i].tag=1;
sort(b+l,b+r+1,cmp2);
cdq2(l,r);
cdq1(mid+1,r);
}
void Mian(){
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i].t>>a[i].x>>a[i].y>>a[i].z>>a[i].w,a[i].id=i;
for(int i=1;i<=n;++i) h[i]=a[i].z;
sort(h+1,h+n+1); tot=unique(h+1,h+n+1)-h-1;
for(int i=1;i<=n;++i) a[i].z=lower_bound(h+1,h+tot+1,a[i].z)-h;
T.n=tot; T.init();
for(int i=1;i<=n;++i) dp[i]=a[i].w;
sort(a+1,a+n+1,cmp1);
cdq1(1,n);
ll ans=-INF;
for(int i=1;i<=n;++i) chkmax(ans,dp[i]);
cout<<ans;
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>c;
while(T--){
Mian();
Mianclr();
}
}
付公主的背包
经典题。作为 gf 入门练习还是不错的。
设体积为 \(v\) 的物品对应的形式幂级数为 \(F_v(x)=\sum_{k \geq 0} x^{vk}=\frac{1}{1-x^v}\),答案就是所有的 \(F_{v_i}(x)\) 卷积后 \(1\sim m\) 项的系数。
直接做时间复杂度爆炸,设答案为 \(Ans(x)=\prod F_{v_i}(x)\),我们希望求 \(\ln\) 后可以把后边的乘法变成加法以简单的运算。
即推导 \(G(x)=\ln F_v(x)\)。
即有 $$G(x)=\int G'(x)=\sum_{k\geq 1} \frac{1}{k} x^{vk}$$
对每个不同的 \(v\) 累计最后 \(\exp\) 一下就得到了 \(Ans(x)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define i28 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e18)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=4e5+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
#define int long long
const int G=3,INVG=332748118;
int f[N],g[N],trs[N];
void NTT(int *f,bool flg,int limit){
for(int i=0;i<limit;++i)
trs[i]=(trs[i>>1]>>1|((i&1)?limit>>1:0));
for(int i=0;i<limit;++i)
if(i<trs[i]) swap(f[i],f[trs[i]]);
for(int p=2;p<=limit;p<<=1){
int len=(p>>1);
int wn=qpow(flg?INVG:G,(MOD-1)/p,MOD);
for(int k=0;k<limit;k+=p){
int buf=1;
for(int i=k;i<k+len;++i){
int tmp=f[i+len]*buf%MOD;
f[i+len]=(f[i]-tmp+MOD)%MOD;
f[i]=(f[i]+tmp)%MOD;
buf=buf*wn%MOD;
}
}
}
if(flg){
int inv=qpow(limit,MOD-2,MOD);
for(int i=0;i<limit;++i) f[i]=f[i]*inv%MOD;
}
}
void INV(int *f,int *g,int n){
static int w[N];
if(n==1){
g[0]=qpow(f[0],MOD-2,MOD);
return ;
}
INV(f,g,(n+1)>>1);
int limit=1;
while(limit<(n<<1)) limit<<=1;
for(int i=0;i<limit;++i) w[i]=f[i];
for(int i=n;i<limit;++i) w[i]=0;
for(int i=(n+1)>>1;i<limit;++i) g[i]=0;
NTT(w,0,limit); NTT(g,0,limit);
for(int i=0;i<limit;++i)
g[i]=(2*g[i]%MOD-g[i]*g[i]%MOD*w[i]%MOD+MOD)%MOD;
NTT(g,1,limit);
for(int i=n;i<limit;++i) g[i]=0;
}
void mul(int *f,int *g,int *h,int n,int m){
int limit=1;
while(limit<(n+m)) limit<<=1;
static int _f[N],_g[N];
for(int i=0;i<n;++i) _f[i]=f[i];
for(int i=0;i<m;++i) _g[i]=g[i];
for(int i=n;i<limit;++i) _f[i]=0;
for(int i=m;i<limit;++i) _g[i]=0;
NTT(_f,0,limit); NTT(_g,0,limit);
for(int i=0;i<limit;++i)
h[i]=_f[i]*_g[i]%MOD;
NTT(h,1,limit);
}
void QiuDao(int *f,int *g,int n){
for(int i=1;i<n;++i) g[i-1]=f[i]*i%MOD;
g[n-1]=0;
}
void JiFen(int *f,int *g,int n){
for(int i=0;i<n;++i) g[i+1]=f[i]*qpow(i+1,MOD-2,MOD)%MOD;
g[0]=0;
}
void Ln(int *f,int *g,int n){
static int _df[N],_if[N],w[N];
for(int i=0;i<(n<<1);++i) _df[i]=_if[i]=w[i]=0;
QiuDao(f,_df,n);
INV(f,_if,n);
mul(_df,_if,w,n,n);
JiFen(w,g,n);
}
void Exp(int *f,int *g,int n){
if(n==1){
g[0]=1;
return ;
}
Exp(f,g,(n+1)>>1);
int limit=1;
while(limit<(n<<1)) limit<<=1;
static int _lng[N],w[N];
for(int i=0;i<limit;++i) _lng[i]=0;
Ln(g,_lng,n);
for(int i=0;i<limit;++i) w[i]=f[i];
for(int i=n;i<limit;++i) w[i]=0;
NTT(w,0,limit); NTT(_lng,0,limit); NTT(g,0,limit);
for(int i=0;i<limit;++i)
g[i]=g[i]*(1-_lng[i]+w[i]+MOD)%MOD;
NTT(g,1,limit);
for(int i=n;i<limit;++i) g[i]=0;
}
int n,m,cnt[N];
void Mian(){
cin>>n>>m;
for(int i=1;i<=n;++i){
int x; cin>>x;
cnt[x]++;
}
for(int i=1;i<=m;++i){
if(!cnt[i]) continue;
for(int j=1;j*i<=m;++j)
(f[j*i]+=cnt[i]*qpow(j,MOD-2,MOD)%MOD)%=MOD;
}
Exp(f,g,m+1);
for(int i=1;i<=m;++i) cout<<g[i]<<'\n';
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>c;
while(T--){
Mian();
Mianclr();
}
}
玩游戏
答案的形式幂级数即为 \(Ans(k)=\frac{\sum_{i=1}^{n}\sum_{j=1}^{m}(a_i+b_j)^k}{nm}\),展开得:
也就是两个形如 \(\sum_{i=1}^n a_i^p\) 的幂级数对应的 EGF 的卷积,即求 \(G(x)=\sum_{k\geq0}(\sum_{i=1}^na_i^k) x^k\) 的前 \(1\sim k\)(题目中给的 \(k\)) 次项系数。还是推式子:
到这里已经可以暴力合并两个分式算了,但是这样每次合并需要七次 dft,常数比较大。
一个很厉害(套路?)的做法是构造 \(\ln (1-xa_i)'=\frac{-a_i}{1-xa_i}\),所以 \(G(x)=-\left(\ln \left(\prod_{i=1}^{n}(1-xa_i)\right)\right)'+n\),里面这个东西分治算就能做到 \(O(n\log^2 n)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define i28 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e18)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=8e5+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
#define int long long
const int G=3,INVG=332748118;
int trs[N];
void NTT(int *f,bool flg,int limit){
for(int i=0;i<limit;++i)
trs[i]=(trs[i>>1]>>1|((i&1)?limit>>1:0));
for(int i=0;i<limit;++i)
if(i<trs[i]) swap(f[i],f[trs[i]]);
for(int p=2;p<=limit;p<<=1){
int len=(p>>1);
int wn=qpow(flg?INVG:G,(MOD-1)/p,MOD);
for(int k=0;k<limit;k+=p){
int buf=1;
for(int i=k;i<k+len;++i){
int tmp=f[i+len]*buf%MOD;
f[i+len]=(f[i]-tmp+MOD)%MOD;
f[i]=(f[i]+tmp)%MOD;
buf=buf*wn%MOD;
}
}
}
if(flg){
int inv=qpow(limit,MOD-2,MOD);
for(int i=0;i<limit;++i) f[i]=f[i]*inv%MOD;
}
}
void INV(int *f,int *g,int n){
static int w[N];
if(n==1){
g[0]=qpow(f[0],MOD-2,MOD);
return ;
}
INV(f,g,(n+1)>>1);
int limit=1;
while(limit<(n<<1)) limit<<=1;
for(int i=0;i<limit;++i) w[i]=f[i];
for(int i=n;i<limit;++i) w[i]=0;
for(int i=(n+1)>>1;i<limit;++i) g[i]=0;
NTT(w,0,limit); NTT(g,0,limit);
for(int i=0;i<limit;++i)
g[i]=(2*g[i]%MOD-g[i]*g[i]%MOD*w[i]%MOD+MOD)%MOD;
NTT(g,1,limit);
for(int i=n;i<limit;++i) g[i]=0;
}
void mul(int *f,int *g,int *h,int n,int m){
int limit=1;
while(limit<(n+m)) limit<<=1;
static int _f[N],_g[N];
for(int i=0;i<n;++i) _f[i]=f[i];
for(int i=0;i<m;++i) _g[i]=g[i];
for(int i=n;i<limit;++i) _f[i]=0;
for(int i=m;i<limit;++i) _g[i]=0;
NTT(_f,0,limit); NTT(_g,0,limit);
for(int i=0;i<limit;++i)
h[i]=_f[i]*_g[i]%MOD;
NTT(h,1,limit);
}
void QiuDao(int *f,int *g,int n){
for(int i=1;i<n;++i) g[i-1]=f[i]*i%MOD;
g[n-1]=0;
}
void JiFen(int *f,int *g,int n){
for(int i=0;i<n;++i) g[i+1]=f[i]*qpow(i+1,MOD-2,MOD)%MOD;
g[0]=0;
}
void Ln(int *f,int *g,int n){
static int _df[N],_if[N],w[N];
for(int i=0;i<(n<<1);++i) _df[i]=_if[i]=w[i]=0;
QiuDao(f,_df,n);
INV(f,_if,n);
mul(_df,_if,w,n,n);
JiFen(w,g,n);
}
int n,m,kk,a[N],b[N],ga[N],gb[N],tmpa[N],tmpb[N];
int fac[N],ifac[N],slen[N],Ans[N];
void solve(int u,int l,int r,int *a,int *F){
slen[u]=r-l+2;
if(l==r){
F[0]=1; F[1]=MOD-a[l];
return ;
}
int mid=l+r>>1,f[N],g[N];
solve(u<<1,l,mid,a,f); solve(u<<1|1,mid+1,r,a,g);
mul(f,g,F,slen[u<<1],slen[u<<1|1]);
}
void Mian(){
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=m;++i) cin>>b[i];
int tmp=qpow(n*m%MOD,MOD-2,MOD),tn=n,tm=m;
cin>>kk; ++kk; chkmax(n,kk); chkmax(m,kk);
fac[0]=1;
for(int i=1;i<=n+m;++i) fac[i]=fac[i-1]*i%MOD;
ifac[n+m]=qpow(fac[n+m],MOD-2,MOD);
for(int i=n+m;i>=1;--i) ifac[i-1]=ifac[i]*i%MOD;
solve(1,1,n,a,tmpa); solve(1,1,m,b,tmpb);
Ln(tmpa,ga,kk); Ln(tmpb,gb,kk);
for(int i=0;i<kk-1;++i){
ga[i]=ga[i+1]*(i+1)%MOD;
gb[i]=gb[i+1]*(i+1)%MOD;
}
ga[kk-1]=gb[kk-1]=0;
for(int i=kk-1;i>=1;--i){
ga[i]=(MOD-ga[i-1])%MOD;
gb[i]=(MOD-gb[i-1])%MOD;
}
ga[0]=tn; gb[0]=tm;
for(int i=2;i<kk;++i){
ga[i]=ga[i]*ifac[i]%MOD;
gb[i]=gb[i]*ifac[i]%MOD;
}
mul(ga,gb,Ans,kk,kk);
for(int i=1;i<kk;++i){
int coef=tmp*fac[i]%MOD;
cout<<coef*Ans[i]%MOD<<'\n';
}
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>c;
while(T--){
Mian();
Mianclr();
}
}
Graph Coloring(Easy ver.)
好题。
首先给出一个结论:若一种颜色的边导出的子图不连通,则另一张颜色的导出子图(其补图)必然是联通的。
所以我们可以直接钦定一种颜色不连通,此时图一定满足题目要求。设 \(dp_n\) 表示 \(n\) 个点的图,钦定蓝色边不连通的总方案数。
直接弄不好转移,图有标号,我们钦定点 \(1\) 所在的极长蓝色连通块大小,不妨设其为 \(j\),则此时这个连通块内部的方案数为 \(dp_j\)(此时此连通块一定是红色不连通,方案数和钦定蓝色不连通相等),对于另外的 \(n-j\) 个点,其与这 \(j\) 个点的连边一定都是红色,且由结论知这 \(n-j\) 个点的导出子图红不连通和蓝不连通都是合法的,故有转移:
\(j=n-1\) 时的 case 即为只剩下一个点。复杂度 \(O(n^2)\)。
AC Code
// Written by Mi2uk1
// Try Harder.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define i28 __int128
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e18)
#define sec second
#define pb push_back
#define eb emplace_back
const int N=5e3+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
int ret=1;
while(b){
if(b&1) ret=1ll*ret*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ret;
}
int n,dp[N],C[N][N];
void Mian(){
cin>>n;
for(int i=0;i<=n;++i){
for(int j=0;j<=i;++j){
if(!j) C[i][j]=1;
else C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}
}
dp[1]=dp[2]=1;
for(int i=3;i<=n;++i){
for(int j=1;j<=i-1;++j){
(dp[i]+=1ll*C[i-1][j-1]*dp[j]%MOD*dp[i-j]%MOD*(2-(j==i-1))%MOD)%=MOD;
}
}
cout<<1ll*(dp[n]-1+MOD)%MOD*2%MOD;
}
void Mianclr(){
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
//freopen("ftc1.in","r",stdin);
//freopen("r.out","w",stdout);
int c,T=1; //cin>>c;
while(T--){
Mian();
Mianclr();
}
}
Graph Coloring(Hard ver.)
\(n\) 变成了 \(5\times 10^4\)。
转移其实就是个全在线卷积,直接做就可以了。
代码有时间来补。咕咕咕。

浙公网安备 33010602011771号