2026.4 - 2026.6 凉雨
优秀的拆分
实际上只需要求以 \(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();
}
}
最长 k 可重区间集问题
注意到一个方案合法等价于我们可以把选的区间分成不超过 \(k\) 组,每组内部没有重叠。
这样的话考虑让流量对应选的组数,每个区间 \(l_i \rightarrow r_i\) 连边,费用为 \(r_i-l_i\),最后限制一下流量不超过 \(k\) 直接上费用流就是对的。
AC Code
#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=1000+9,M=1e5+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 int long long
int mxn,flow,cost;
int h[N],edgecnt=2;
//remember to reset the edgecnt
struct edge{
int to,val,nxt,cost;
}e[M<<1];
void add2(int u,int v,int w,int c){
e[edgecnt]={v,w,h[u],c};
h[u]=edgecnt++;
}
void add(int u,int v,int w,int c){
add2(u,v,w,c);
add2(v,u,0,-c);
}
int s,t,maxflow,mincost;
int pre[N],dis[N];
bitset<N> inq;
bool spfa(){
fill(dis,dis+mxn+2,LLONG_MAX);
memset(pre,0,sizeof(pre));
queue<int> q;
q.push(s);
dis[s]=0;
inq[s]=1;
while(!q.empty()){
int u=q.front();
q.pop();
inq[u]=0;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
if(e[i].val>0&&dis[v]>dis[u]+e[i].cost){
dis[v]=dis[u]+e[i].cost;
pre[v]=i;
if(!inq[v]){
q.push(v);
inq[v]=1;
}
}
}
}
return dis[t]!=LLONG_MAX;
}
void EK(){
while(spfa()){
int flow=LLONG_MAX;
for(int i=pre[t];i;i=pre[e[i^1].to]){
flow=min(flow,e[i].val);
}
for(int i=pre[t];i;i=pre[e[i^1].to]){
e[i].val-=flow;
e[i^1].val+=flow;
}
maxflow+=flow;
mincost+=flow*dis[t];
}
}
int n,k,L[N],R[N];
int tot,b[N*2];
void Mian(){
cin>>n>>k;
for(int i=1;i<=n;++i) cin>>L[i]>>R[i];
for(int i=1;i<=n;++i) b[++tot]=L[i],b[++tot]=R[i];
sort(b+1,b+tot+1);
tot=unique(b+1,b+tot+1)-b-1;
for(int i=1;i<=n;++i)
L[i]=lower_bound(b+1,b+tot+1,L[i])-b,
R[i]=lower_bound(b+1,b+tot+1,R[i])-b;
mxn=tot;
s=0,t=tot+1;
add(s,1,k,0);
add(tot,t,INF,0);
for(int i=1;i<tot;++i) add(i,i+1,INF,0);
for(int i=1;i<=n;++i) add(L[i],R[i],1,b[L[i]]-b[R[i]]);
EK();
cout<<-mincost;
}
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();
}
}
剪刀石头布
神题。
先考虑三个点,如果这三个点没有形成三元环,那么必然存在一个点出度为 \(2\),一个点入度为 \(2\),一个点入读出度都为 \(1\)。
这样一个入度为 \(k\) 的点就一定对应损失了 \(\frac{k\times (k-1)}{2}\) 个三元环。设 \(deg_i\) 为 \(i\) 的入度,则答案就是 \(\frac{n\times (n-1)\times (n-2)}{6}-\sum\frac{deg_i\times (deg_i-1)}{2}\),也就是我们要求后面那个式子的最小值。
接下来就是把一些入度分配给一些点,使 \(\sum\frac{deg_i\times (deg_i-1)}{2}\) 最小。令 \(F(k)=\frac{k\times (k-1)}{2}\),套路地在把每个点拆成 \(in_i,out_i\),并在中间建 \(n\) 个点,连边容量为 \(1\),费用分别为 \(F(1),F(2)-F(1),F(3)-F(2),\dots\),由于 \(F\) 是凸的,所以自动选择费用小的点走时正确性也是对的。
这样直接跑费用流就对了,非常厉害。
AC Code
#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=300+9,M=1e5+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 int long long
int n,m,flow,cost,a[N][N];
vector<pii> din[N];
struct MincostMaxflow{
#define MAXN (int)3e4+9
#define MAXM (int)1e5+9
int head[MAXN],tot=1;
//remember to reset the tot
int n,nxt[MAXM*2],to[MAXM*2],cost[MAXM*2],w[MAXM*2];
void add2(int u,int v,int c,int flow){
nxt[++tot]=head[u];
head[u]=tot; to[tot]=v;
cost[tot]=c; w[tot]=flow;
}
void add(int u,int v,int flow,int c){
add2(u,v,c,flow);
add2(v,u,-c,0);
}
int s,t,mxflow,micost;
bool vis[MAXN];
int h[MAXN];
void spfa(){
for(int i=1;i<=n;++i)
h[i]=INF,vis[i]=0;
queue<int> q;
h[s]=0;
vis[s]=1; q.push(s);
while(!q.empty()){
int u=q.front(); q.pop();
vis[u]=0;
for(int i=head[u];i;i=nxt[i]){
if(w[i] && h[to[i]]>h[u]+cost[i]){
h[to[i]]=h[u]+cost[i];
if(!vis[to[i]]){
q.push(to[i]);
vis[to[i]]=1;
}
}
}
}
}
int dis[MAXN],cur[MAXN];
bool Dijkstra(){
for(int i=1;i<=n;++i)
dis[i]=INF,vis[i]=0;
priority_queue<pii> q;
dis[s]=0; q.push({0,s});
while(!q.empty()){
int u=q.top().sec; q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=nxt[i]){
if(w[i] && dis[to[i]]>dis[u]+cost[i]+h[u]-h[to[i]]){
dis[to[i]]=dis[u]+cost[i]+h[u]-h[to[i]];
q.push({-dis[to[i]],to[i]});
}
}
}
return dis[t]!=INF;
}
int dfs(int u,int flow){
if(u==t) return flow;
int out=0;
vis[u]=1;
for(int i=cur[u];i && flow;i=nxt[i]){
cur[u]=i;
if(w[i] && !vis[to[i]] && h[to[i]]==h[u]+cost[i]){
int x=dfs(to[i],min(w[i],flow));
w[i]-=x; w[i^1]+=x;
flow-=x; out+=x;
micost+=cost[i]*x;
}
}
vis[u]=0;
return out;
}
pii dinic(){
mxflow=micost=0;
spfa();
while(Dijkstra()){
for(int i=1;i<=n;++i) h[i]=h[i]+dis[i];
for(int i=1;i<=n;++i) cur[i]=head[i],vis[i]=0;
mxflow+=dfs(s,INF);
}
return {mxflow,micost};
}
}G;
int deg[N],tot;
int F(int x){return x*(x-1)/2;}
int id(int i,int j){return (i-1)*(n+2)+j;}
void Mian(){
cin>>n;
tot=n*(n+2); G.s=++tot,G.t=++tot;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j)
G.add(id(i,n+1),id(i,j),1,F(j)-F(j-1));
for(int j=1;j<=n;++j)
G.add(id(i,j),id(i,n+2),1,0);
G.add(id(i,n+2),G.t,INF,0);
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
int k; cin>>k;
a[i][j]=k;
if(i>=j) continue;
if(k==0) G.add(G.s,id(i,n+1),1,0);
if(k==1) G.add(G.s,id(j,n+1),1,0);
if(k==2){
int uu=++tot;
G.add(G.s,tot,1,0);
G.add(tot,id(i,n+1),1,0); din[i].pb({j,G.tot});
G.add(tot,id(j,n+1),1,0); din[j].pb({i,G.tot});
}
}
}
G.n=tot;
cout<<n*(n-1)*(n-2)/6-G.dinic().sec<<'\n';
for(int i=1;i<=n;++i){
for(auto y:din[i]){
int v=y.fir,id=y.sec;
if(G.w[id]) a[v][i]=1;
else a[v][i]=0;
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j) cout<<a[i][j]<<' ';
cout<<'\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();
}
}
Binary Tree on Plane
拆点,把每个点拆成 \(in_i,out_i\)。
让流量表示儿子数量:
- \(s \rightarrow in_i\),容量为 \(2\),费用为 \(0\)。
- \(out_i \rightarrow t\),容量为 \(1\),费用为 \(0\)。
- 同时对于每一组合法的 \(u\rightarrow v\),让 \(in_u \rightarrow out_v\),容量为 \(1\),费用为 \(u,v\) 的欧几里得距离。
这样最大流就表示了这棵树最大的合法边数,显然不为 \(n-1\) 则无解,同时最小费用也就是这棵树的最小边权和。
AC Code
#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=1000+9,M=1e5+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,m,flow,h[N];
double mincost;
int edgecnt=2;
//remember to reset the edgecnt
struct edge{
int to,val,nxt;
double cost;
} e[M<<1];
void add2(int u,int v,int w,double c){
e[edgecnt]={v,w,h[u],c};
h[u]=edgecnt++;
}
void add(int u,int v,double c,int w){
add2(u,v,w,c);
add2(v,u,0,-c);
}
int s,t,maxflow;
int pre[N];
double dis[N];
bitset<N> inq;
bool spfa(){
for(int i=1;i<=n+2;++i) dis[i]=INF;
memset(pre,0,sizeof(pre));
queue<int> q;
q.push(s);
dis[s]=0;
inq[s]=1;
while(!q.empty()){
int u=q.front();
q.pop();
inq[u]=0;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
if(e[i].val>0 && dis[v]>dis[u]+e[i].cost){
dis[v]=dis[u]+e[i].cost;
pre[v]=i;
if(!inq[v]){
q.push(v);
inq[v]=1;
}
}
}
}
return dis[t]!=INF;
}
void EK(){
while(spfa()){
int flow=INF;
for(int i=pre[t];i;i=pre[e[i^1].to]){
flow=min(flow,e[i].val);
}
for(int i=pre[t];i;i=pre[e[i^1].to]){
e[i].val-=flow;
e[i^1].val+=flow;
}
maxflow+=flow;
mincost+=flow*dis[t];
}
}
struct node{
double x,y;
} a[N];
double dist(int x,int y){
return 1.0*sqrt((a[x].x-a[y].x)*(a[x].x-a[y].x)+
(a[x].y-a[y].y)*(a[x].y-a[y].y));
}
void Mian(){
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i].x>>a[i].y;
s=2*n+1; t=2*n+2;
for(int i=1;i<=n;++i){
add(s,i,0,2);
add(i+n,t,0,1);
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(a[j].y<a[i].y)
add(i,j+n,dist(i,j),1);
}
}
int tmp=n;
n=2*n+2; EK();
if(maxflow!=tmp-1) cout<<-1;
else printf("%.15lf",mincost);
}
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();
}
}
善意的投票
集合划分模型。
考虑一个点与 \(S\) 联通表示同意睡午觉,与 \(T\) 联通表示不同意睡午觉。这样对于一个同意的人 \(i\),如果他和 \(T\) 相连(改变了意愿)则需要付出 \(1\) 的代价,连边 \(S \rightarrow i\),容量为 \(1\)。不同意的人同理连边 \(i\rightarrow T\),容量为 \(1\)。
对于一对好朋友,如果不相同(一个连 \(S\) 一个连 \(T\))则需要付出 \(1\) 的代价。直接连双向边,容量均为 \(1\) 即可,我们总是会切断从连 \(S\) 的点到连 \(T\) 的点的那条边。
这样答案就是这个图最小割,即求出此图的最大流即可。
AC Code
#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=300+9,M=1e5+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 Dinic{
#define MAXN 10000+9
#define MAXM 100000*2+9
int n,m,s,t;
int head[MAXN],tot=1;
int nxt[MAXM],to[MAXM],w[MAXM];
void add2(int u,int v,int x){
nxt[++tot]=head[u];
head[u]=tot;
to[tot]=v;
w[tot]=x;
}
void add(int u,int v,int x){
add2(u,v,x);
add2(v,u,0);
}
int dep[N],cur[N];
queue<int> q;
bool bfs(){
for(int i=1;i<=n;++i) dep[i]=0;
for(int i=1;i<=n;++i) cur[i]=head[i];
dep[s]=1; q.push(s);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i;i=nxt[i]){
if(!dep[to[i]] && w[i]){
dep[to[i]]=dep[u]+1;
q.push(to[i]);
}
}
}
return dep[t];
}
int dfs(int u,int flow){
if(u==t) return flow;
int out=0;
for(int i=cur[u];i && flow;i=nxt[i]){
cur[u]=i;
if(w[i] && dep[to[i]]==dep[u]+1){
int x=dfs(to[i],min(flow,w[i]));
flow-=x; out+=x;
w[i]-=x; w[i^1]+=x;
}
}
return out;
}
int solve(){
int ans=0;
while(bfs())
ans+=dfs(s,INF);
return ans;
}
} G;
void Mian(){
cin>>G.n>>G.m;
G.s=G.n+1,G.t=G.n+2;
for(int i=1;i<=G.n;++i){
int k; cin>>k;
if(k) G.add(G.s,i,1);
else G.add(i,G.t,1);
}
for(int i=1;i<=G.m;++i){
int u,v; cin>>u>>v;
G.add(u,v,1);
G.add(v,u,1);
}
G.n+=2;
cout<<G.solve();
}
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();
}
}
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号