D. Disposable Switches
显然最终的答案只与经过的边数和经过的最短路径长度和有关
令(dis[x][k])表示到(x)点经过(k)条边的最短路(因为记录了边数,这个东西可以直接(n^2)预处理
完全不可能的点不容易求,考虑求哪些点有可能在(1 ightarrow n)的最短路上
考虑所有(dis[n][x]),对应的答案即为(res=ccdot x+frac{dis[n][x]}{v})
相当于求对所有(<c,v>)的可能取值,有哪些(x)对应的(res)有可能对应最小值
不妨固定(v),此时令(y=frac{dis[n][x]}{v}),变形得:(y=-ccdot x+res),是一个直线的式子
只需要考虑对所有(cge 0),哪些点((x,y))可能对应(res_{min})
显然只需要求一个下凸壳,同时由于(cge 0)只需要这个下凸壳中斜率小于0的部分
求出所有可能的((x,y))后,所有能转移出((x,dis[n][x]))的点都是有用的
倒着(dfs)一遍并打标记即可
#include<bits/stdc++.h>
#define ll long long
#define db double
#define MAXN 100100
#define pii pair<int,int>
#define fi first
#define se second
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0',ch=getchar();}
return x*f;
}
ll inf=1e18;
int n,m,nxt[20010],fst[2020],to[20010],cnt,val[20010];
ll dis[2020][2020];
int st[2020],tp,ok[2020];
pair<int,ll> g[2020];
void add(int u,int v,int w){nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v,val[cnt]=w;}
void getdis()
{
rep(i,1,n) rep(j,0,n) dis[i][j]=inf;dis[1][0]=0;
rep(k,0,n) rep(x,1,n-1) if(dis[x][k]!=inf)
ren if(dis[to[i]][k+1]>dis[x][k]+val[i])
dis[to[i]][k+1]=dis[x][k]+val[i];
}
inline ll Y(int a,int b){return g[b].se-g[a].se;}
inline ll X(int a,int b){return g[b].fi-g[a].fi;}
int tag[2020],ans;
void dfs(int x,int num)
{
if(tag[x]) return ;tag[x]=1;
ok[x]=1;if(!num) return ;
ren if(dis[to[i]][num-1]+val[i]==dis[x][num]) dfs(to[i],num-1);
}
int main()
{
n=read(),m=read();int x,a,b,c;
rep(i,1,m) a=read(),b=read(),c=read(),add(a,b,c),add(b,a,c);
getdis();m=0;
rep(i,1,n) if(dis[n][i]!=inf) g[++m]={i,dis[n][i]};
tp=0;rep(i,1,m)
{
while(tp>=2&&Y(st[tp],i)*X(st[tp-1],st[tp])<Y(st[tp-1],st[tp])*X(st[tp],i))
tp--;
st[++tp]=i;
}
while(tp>=2&&Y(st[tp-1],st[tp])>0) tp--;
rep(i,1,tp)
{
rep(j,1,n) tag[j]=0;
dfs(n,g[st[i]].first);
}
rep(i,1,n) if(!ok[i]) ans++;
printf("%d
",ans);
rep(i,1,n) if(!ok[i]) printf("%d ",i);
}
H. Height Profile
最终答案区间显然至少有一个端点是整点,否则可以通过向斜率更大的区间平移得到更大答案
则枚举左端点的整点(l),找到右侧满足条件的最远整点(r),即(h_r-h_lge(r-l)g)
移项得:(h_r-rcdot gge h_l-lcdot g),令(w_i=h_i-icdot g),在线段树上二分容易得到(r)
对于这个([l,r])分别计算向左延伸和向右延伸的答案即可
#include<bits/stdc++.h>
#define ll long long
#define db double
#define inf 2139062143
#define MAXN 100100
#define pii pair<int,int>
#define fi first
#define se second
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n,m,res,h[MAXN],g,mx[MAXN<<2],w[MAXN];
db ans;
void build(int k,int l,int r)
{
if(l==r) {w[l]=mx[k]=h[l]-g*l;return ;}int mid=l+r>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
void find(int k,int l,int r,int a,int b,db w)
{
if(a<=l&&r<=b)
{
if(mx[k]<w) return ;
if(l==r) {res=max(l,res);return ;}
int mid=l+r>>1;
if(mx[k<<1|1]>=w) find(k<<1|1,mid+1,r,a,b,w);
else find(k<<1,l,mid,a,b,w);
return ;
}
int mid=l+r>>1;
if(a<=mid) find(k<<1,l,mid,a,b,w);
if(b>mid) find(k<<1|1,mid+1,r,a,b,w);
}
int main()
{
n=read(),m=read();rep(i,0,n) h[i]=read();
int l,r;db tmp;
while(m--)
{
scanf("%lf",&tmp);g=floor(tmp*10+0.1);
build(1,0,n);ans=-1;
rep(i,0,n-1)
{
res=0;find(1,0,n,i+1,n,w[i]);
if(!res) continue;
l=i,r=res;ans=max(ans,(db)res-i);
if(l&&w[l-1]>w[r])
{
tmp=(1.0*w[r]-w[l])/(1.0*w[l-1]-w[l]);
ans=max(ans,res-i+tmp);
}
if(r<n)
{
tmp=(1.0*w[r]-w[l])/(1.0*w[r]-w[r+1]);
ans=max(ans,res-i+tmp);
}
}
printf("%.8lf
",ans);
}
}
J. Jackdaws And Crows
枚举(fake accounts)的个数(x),则所有绝对值小于(x)的位置的取值可正可负,视为是自由点
显然只有当(x)为原序列中某数绝对值(+1)时,会影响自由点
将原序列按照绝对值排序,枚举时可以用链表快速维护剩余的非自由点
问题转化为如何快速求在当前这个状态需要删除多少个数使满足条件
- 显然删除非自由点比删除自由点更优
- 最后需要删除的个数即为在链表中相邻的非自由点中的矛盾对数,可以简单证明:令非自由点序列为(a_i),当(<a_i,a_{i+1}>)矛盾时,显然(<a_i,a_{k}>)与(<a_{i+1},a_k>)一定只有一个矛盾,其中(k> i+1)。当选择删除(a_{i+1})时,(<a_i,a_k>)的矛盾因为后面部分的移动发生变化,而变化后一定与原先的(<a_{i+1},a_k>)相同。即:每次删除一个非自由点之后只能使相邻的矛盾对数减一,因此最终答案为链表中相邻的非自由点中的矛盾对数。
这个答案在初始(O(n))统计一次答案之后,每次删除一个数的时候可以(O(1))快速维护
(初始存在0的时候,需要考虑将0全部删掉的情况
#include<bits/stdc++.h>
#define ll long long
#define db double
#define MAXN 500100
#define pii pair<int,int>
#define fi first
#define se second
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n,res,las,l[MAXN],r[MAXN];
ll p,q,a[MAXN],ans;
pii g[MAXN];
inline int calc(int x,int y)
{
if(!(x*y)) return 0;
return (a[x]*a[y]>0)^((y-x+1)&1);
}
int main()
{
n=read(),p=read(),q=read();rep(i,1,n) a[i]=read(),g[i]={abs(a[i]),i};
rep(i,1,n)
{
if(a[i]==0) {res++;continue;}
if(las&&a[las]*a[i]>0) res++;
las=i;
}
ans=res*q;sort(g+1,g+n+1);
las=res=0;int x;
rep(i,1,n) if(a[i])
{if(las) r[las]=i,res+=calc(las,i);l[i]=las,las=i;}
ans=min(ans,q*res+p);
rep(i,1,n) if(g[i].fi)
{
x=g[i].se;res-=calc(l[x],x)+calc(x,r[x])-calc(l[x],r[x]);
if(r[x]) l[r[x]]=l[x];if(l[x]) r[l[x]]=r[x];
ans=min(ans,p*(g[i].fi+1)+q*res);
}
printf("%lld
",ans);
}
C. Mean Streets of Gadgetzan
记录有哪些点的值已经确定,同时由于推出的条件一定是成立,记录每个点在哪些推出语句中
若该点被确定成立后使整个推出语句成立,则将被推出的点也被确定
(bfs)模拟一下过程,中间出现矛盾直接退出
最终答案中没有被赋值的点可以赋为0不影响答案
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 1001001
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
namespace CALC
{
inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
inline int mul(int a,int b){return (1LL*a*b)%MOD;}
inline void inc(int &a,int b){a=pls(a,b);}
inline void dec(int &a,int b){a=mns(a,b);}
inline void tms(int &a,int b){a=mul(a,b);}
inline int qp(int x,int t,int res=1)
{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,m,num[MAXN],to[MAXN],cur[MAXN],len,st[MAXN],tp,q[MAXN],hd=1,tl;
vector<int> in[MAXN];
char s[MAXN*6];
void mdf(int x,int w)
{
if(cur[x]>=0&&cur[x]+w==1) {puts("conflict");exit(0);}
if(w>0&&cur[x]<0) q[++tl]=x;cur[x]=w;
}
int main()
{
m=read(),n=read();int flg=1,x=0;Fill(cur,0xff);
rep(i,1,m)
{
gets(s+1);len=strlen(s+1);s[++len]='
';
tp=x=0;flg=1;
rep(j,1,len)
{
if(isdigit(s[j])) x=x*10+s[j]-'0';
else if(s[j]=='!') flg=-1,x=0;
else if(isdigit(s[j-1])) st[++tp]=x*flg,flg=1,x=0;
}
if(tp==1) {mdf(abs(st[1]),st[1]>0);continue;}
num[i]=tp-1;
rep(j,1,tp-1) in[st[j]].pb(i);
to[i]=st[tp];
}
while(hd<=tl)
{
x=q[hd++];
for(auto v:in[x]) if(!(--num[v]))
mdf(abs(to[v]),to[v]>0);
}
rep(i,1,n) if(cur[i]==1) putchar('T');else putchar('F');
}
D. Hard Nim
本题需要求出每个数是必胜还是必败
首先二进制有奇数个(1)的数是必胜态,即上一个人留下了奇数个(1)
只需关心二进制有偶数个(1)的数,考虑如何转移
令(x)为必败,([x+1,x+m])内的数显然均为必胜,则第一个(>x+m)且二进制有偶数个(1)的数为必败
如此即得到了一个(O(frac{n}{m}))的做法
考虑分块,每(K=2^{20})个数为一个块,这样的块只有两种,即位数(20+)的(1)的个数的奇偶性
若能(O(1))地从一个块转移到下一个块,复杂度降为(O(frac{n}{K}+K))
这一部分可以预处理得到,每个块内只需预处理前(m+2)个数
枚举该块与下一块的奇偶性,暴力向后跳即可得到,该部分复杂度为(O(m imes frac{K}{m})=O(K))
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define vi vector<int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
namespace CALC
{
inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
inline int mul(int a,int b){return (1LL*a*b)%MOD;}
inline void inc(int &a,int b){a=pls(a,b);}
inline void dec(int &a,int b){a=mns(a,b);}
inline void tms(int &a,int b){a=mul(a,b);}
inline int qp(int x,int t,int res=1)
{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
const int tot=1<<20,MAXN=tot+100;
int m,nxt[4][MAXN];
ll n;
inline int calc(ll x,int res=0){rep(i,0,19) if((x>>i)&1) res^=1;return res;}
int main()
{
n=read(),m=read();int x,y;ll p,tmp=0;
rep(i,0,3) rep(j,0,m+3)
{
p=j;x=i>>1,y=i&1;
if(x^calc(j)) continue;
while(p<tot)
{
p+=m+1;
while((p<tot?x:y)^calc(p)) ++p;
}
nxt[i][j]=p-tot;
}
for(ll i=p=0;i+tot<=n;i+=tot,tmp=i)
p=nxt[(calc(i>>20LL)<<1)|calc(i+tot>>20LL)][p];
n-=tmp;tmp=calc(tmp>>20LL);
while(p<n)
{
p+=m+1;
while(tmp^calc(p)) ++p;
}
puts(p==n?"zyw":"lyw");
}
I. Broken routers
首先将所有询问离线下来按时间排序
当询问点在基环树上的树部分时,需要每个点子树内深度为(x)的点权和,此部分可以用长链剖分维护
使用长链剖分需要将询问挂到点上,在(dfs)过程中统计答案
而对于环来说普通的循环容易维护,只需要考虑每个环外点的进入环的影响
根据该点的深度计算出进入环的位置,动态维护一个环上相对顺序的权值数组
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
namespace CALC
{
inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
inline int mul(int a,int b){return (1LL*a*b)%MOD;}
inline void inc(int &a,int b){a=pls(a,b);}
inline void dec(int &a,int b){a=mns(a,b);}
inline void tms(int &a,int b){a=mul(a,b);}
inline int qp(int x,int t,int res=1)
{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,m,to[MAXN],tag[MAXN],vis[MAXN],tot;
int id[MAXN],mxd[MAXN],frm[MAXN],lgs[MAXN],dep[MAXN],bl[MAXN],w[MAXN],p[MAXN];
ll ans[MAXN],*f[MAXN],tmp[MAXN<<2],*cur=tmp;
vector<int> G[MAXN],cir[MAXN];
vector<ll> now[MAXN];
vector<pii> vq[MAXN];
struct Ask{int t,x,id;}q[MAXN];
bool operator < (const Ask &x,const Ask &y){return x.t<y.t;}
bool cmp(int x,int y){return dep[x]<dep[y];}
void find(int x,int anc)
{
if(vis[x]==anc)
{
cir[++tot].pb(x),now[tot].pb(w[x]);frm[x]=tot,tag[x]=1,id[x]=0;
for(int tmp=x;to[tmp]!=x;tmp=to[tmp])
id[to[tmp]]=id[tmp]+1,cir[tot].pb(to[tmp]),
tag[to[tmp]]=1,frm[to[tmp]]=tot,now[tot].pb(w[to[tmp]]);
return ;
}
else if(vis[x]) return ;
vis[x]=anc;find(to[x],anc);
}
void dfs(int x,int anc)
{
for(auto v:G[x])
{
dep[v]=dep[x]+1;dfs(v,anc);
if(mxd[v]>mxd[lgs[x]]) lgs[x]=v;
}
bl[x]=anc,mxd[x]=mxd[lgs[x]]+1;
}
inline void New(int x){f[x]=cur,cur+=mxd[x]<<1;}
void getf(int x)
{
if(lgs[x]){f[lgs[x]]=f[x]+1;getf(lgs[x]);}
f[x][0]=w[x];
for(auto v:G[x]) if(v^lgs[x])
{New(v);getf(v);rep(i,0,mxd[v]) f[x][i+1]+=f[v][i];}
for(auto t:vq[x])
ans[t.se]=t.fi>mxd[x]?0:f[x][t.fi];
}
void mdf(int x)
{
if(!dep[x]) return ;int y=bl[x],len=cir[frm[y]].size();
now[frm[y]][((id[y]-dep[x])%len+len)%len]+=w[x];
}
int main()
{
n=read();rep(i,1,n) w[i]=read();rep(i,1,n) to[i]=read();
m=read();rep(i,1,m) q[i].x=read(),q[i].t=read(),q[i].id=i;
sort(q+1,q+m+1);
rep(i,1,n) if(!vis[i]) find(i,i);
rep(i,1,m) if(!tag[q[i].x]) vq[q[i].x].pb({q[i].t,q[i].id});
rep(i,1,n) if(!tag[i]) G[to[i]].pb(i);
rep(i,1,n) if(tag[i]) dfs(i,i),New(i),getf(i);
rep(i,1,n) p[i]=i;int pos=1,x,len;
sort(p+1,p+n+1,cmp);
rep(i,1,m) if(tag[q[i].x])
{
while(dep[p[pos]]<=q[i].t&&pos<=n) mdf(p[pos]),pos++;
x=q[i].x,len=cir[frm[x]].size();
ans[q[i].id]=now[frm[x]][((id[x]-q[i].t)%len+len)%len];
}
rep(i,1,m) printf("%lld
",ans[i]);
}
A. Accelerator
考虑一个长度为(i)的段,容易得到这一段的贡献为:(i!(n-i)!prod b_j)
令(F_i=sumlimits_{jsubsetneq S,|j|=i} prod_{xin j}a_x),则答案为(frac{1}{n!}sumlimits_{i=1}^ni!(n-i)!F_i)
根据(F_i)的意义,考虑生成函数,(F_i)即为([x^i]left[(1+a_1x)(1+a_2x)cdots (1+a_nx) ight])
分治+(ntt)即可
(本题并不困难,mark一下卡常技巧:当卷积的两个多项式其中一个长度较小时,直接相乘优于卷积
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 200100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define vi vector<int>
#define fi first
#define se second
#define pb push_back
#define Clear(x) {x.clear();vector<int> (x).swap(x);}
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
namespace CALC
{
inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:a+b;}
inline int mns(int a,int b){return a-b<0?a-b+MOD:a-b;}
inline int mul(int a,int b){return 1LL*a*b%MOD;}
inline void inc(int &a,int b){a=pls(a,b);}
inline void dec(int &a,int b){a=mns(a,b);}
inline void tms(int &a,int b){a=mul(a,b);}
inline int qp(int x,int t,int res=1)
{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,ans,g[MAXN],pw[30],ipw[30];
int fac[MAXN],ifac[MAXN],q[MAXN<<2];
vector<int> ret;
namespace Poly
{
int rev[MAXN<<2];
vi res;
int mem(int n)
{
int lg=1,lim=1;
for(;lim<n;lim<<=1,lg++);
rep(i,0,lim-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-2));
return lim;
}
void ntt(int *a,int n,int f)
{
rep(i,0,n-1) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=1,t=1;i<n;i<<=1,++t)
{
int wn= f>0?pw[t]:ipw[t];for(int j=0;j<n;j+=i<<1)
{
int w=1,x,y;for(int k=0;k<i;++k,w=mul(w,wn))
x=a[j+k],y=mul(a[j+k+i],w),a[j+k]=pls(x,y),a[j+k+i]=mns(x,y);
}
}
if(f>0) return ;int nv=Inv(n);rep(i,0,n-1) a[i]=mul(a[i],nv);
}
vi Mul(const vi &a,const vi &b)
{
static int A[MAXN<<2],B[MAXN<<2];
int n=a.size(),m=b.size(),s=mem(n+m-1);
if(min(n,m)<128)
{
res.assign(n+m-1,0);
rep(i,0,n-1) rep(j,0,m-1) inc(res[i+j],mul(a[i],b[j]));
return res;
}
rep(i,0,n-1) A[i]=a[i];rep(i,n,s-1) A[i]=0;
rep(i,0,m-1) B[i]=b[i];rep(i,m,s-1) B[i]=0;
ntt(A,s,1);ntt(B,s,1);
rep(i,0,s-1) tms(A[i],B[i]);
ntt(A,s,-1);
res.resize(n+m);rep(i,0,n+m-1) res[i]=A[i];
return res;
}
vi solve(int l,int r)
{
if(l==r) return {1,g[l]};
int mid=l+r>>1;
return Mul(solve(l,mid),solve(mid+1,r));
}
}
void init(int n)
{
fac[0]=ifac[0]=1;rep(i,1,n) fac[i]=mul(fac[i-1],i);
ifac[n]=Inv(fac[n]);dwn(i,n-1,1) ifac[i]=mul(ifac[i+1],i+1);
rep(i,1,25) pw[i]=qp(3,(MOD-1)/(1<<i)),ipw[i]=Inv(pw[i]);
}
int main()
{
init(1e5);rep(T,1,read())
{
n=read(),ans=0;rep(i,1,n) g[i]=read();
ret=Poly::solve(1,n);
rep(i,1,n) inc(ans,mul(ret[i],mul(fac[i],fac[n-i])));
printf("%d
",mul(ans,ifac[n]));
}
}
C. Club Assignment
将所有边升序排列,加入每条边,将该边两个点分在两个集合内。若有一边两点已经在同一集合内,则该边权值即为答案
实际上显然不可能将所有边都存下来
注意到上述过程上与生成树过程无异,只需要求出生成树之后再对两种颜色内部分别计算一下贡献
现在只需要求出一个异或最小生成树
实际上是利用(boruvka)这一算法,由高至低每一位对(0,1)两个子树间找一条尽可能小的边
可以访问某一子树中所有点在另一子树内查询,容易实现
总复杂度(O(nlog^2w))
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
namespace CALC
{
inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
inline int mul(int a,int b){return (1LL*a*b)%MOD;}
inline void inc(int &a,int b){a=pls(a,b);}
inline void dec(int &a,int b){a=mns(a,b);}
inline void tms(int &a,int b){a=mul(a,b);}
inline int qp(int x,int t,int res=1)
{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,tr[MAXN*15][2],tot,l[MAXN*15],r[MAXN*15],col[MAXN],ans;
vector<int> G[MAXN];
pii a[MAXN];
void add(int u,int v){G[u].pb(v);G[v].pb(u);}
void ins(int x,int id)
{
int pos=0,t;dwn(i,29,0)
{
t=(x>>i)&1;if(!tr[pos][t]) tr[pos][t]=++tot;
pos=tr[pos][t];
if(!l[pos]) l[pos]=id;r[pos]=id;
}
}
int query(int x,int rt,int dep)
{
if(dep<0||l[rt]==r[rt]) return l[rt];
int t=(x>>dep)&1;
return tr[rt][t]?query(x,tr[rt][t],dep-1):query(x,tr[rt][t^1],dep-1);
}
int queryw(int x,int res=0)
{
if(!tr[0][0]&&!tr[0][1]) return inf;
int pos=0,t;dwn(i,29,0)
{
t=(x>>i)&1;
if(tr[pos][t]) pos=tr[pos][t];
else pos=tr[pos][t^1],res|=1<<i;
}
return res;
}
void solve(int rt,int dep)
{
if(dep<0||l[rt]==r[rt]||l[rt]==r[rt]+1)
{rep(i,l[rt],r[rt]-1) add(a[i].se,a[i+1].se);return ;}
if(tr[rt][0]&&tr[rt][1])
{
int res=inf,tmp;pii g;
rep(i,l[tr[rt][0]],r[tr[rt][0]])
{
tmp=query(a[i].fi,tr[rt][1],dep-1);
if((a[i].fi^a[tmp].fi)<res) res=a[i].fi^a[tmp].fi,g={i,tmp};
}
add(a[g.fi].se,a[g.se].se);
solve(tr[rt][0],dep-1);solve(tr[rt][1],dep-1);
}
else if(tr[rt][0]) solve(tr[rt][0],dep-1);
else solve(tr[rt][1],dep-1);
}
void dfs(int x,int pa){col[x]=col[pa]^1;for(auto v:G[x]) if(v^pa) dfs(v,x);}
int main()
{
rep(T,1,read())
{
n=read(),ans=inf;rep(i,1,n) a[i].fi=read(),a[i].se=i;
sort(a+1,a+n+1);rep(i,1,n) ins(a[i].fi,i);
l[0]=1,r[0]=n;solve(0,29);dfs(1,0);
rep(i,1,n) G[i].clear();
for(;~tot;tot--) l[tot]=tr[tot][0]=tr[tot][1]=0;tot=0;
rep(i,1,n) if(!col[a[i].se]) {ans=min(ans,queryw(a[i].fi));ins(a[i].fi,0);}
for(;~tot;tot--) tr[tot][0]=tr[tot][1]=0;tot=0;
rep(i,1,n) if(col[a[i].se]) {ans=min(ans,queryw(a[i].fi));ins(a[i].fi,0);}
for(;~tot;tot--) tr[tot][0]=tr[tot][1]=0;tot=0;
printf("%d
",ans);
rep(i,1,n) putchar(col[i]?'2':'1');puts("");
}
}
J. Jewel Grab
由于查询时的(k)很小,因为若能快速查找到区间内具有相同颜色的块,则可以暴力向后跳计算贡献
考虑对于每个元素(a_i)维护(pre_i),表示上一个与其颜色相同的元素的下标
每次查找即为在([l,r])内查询第一个(pre_i>x)的(i),用线段树维护(pre)然后线段树二分即可
对于修改,可以对每个颜色维护一个(set),通过(upper\_bound)函数快速找到后继和前驱进行修改
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 200100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
namespace CALC
{
inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
inline int mul(int a,int b){return (1LL*a*b)%MOD;}
inline void inc(int &a,int b){a=pls(a,b);}
inline void dec(int &a,int b){a=mns(a,b);}
inline void tms(int &a,int b){a=mul(a,b);}
inline int qp(int x,int t,int res=1)
{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,m,col[MAXN],w[MAXN],pre[MAXN],pos[MAXN];
int mx[MAXN<<2],mxc[MAXN];
ll c[MAXN],ans;
inline void mdf(int x,int w){for(;x<=n;x+=x&-x) c[x]+=w;}
inline ll query(int x,ll res=0){for(;x;x-=x&-x) res+=c[x];return res;}
inline ll getsum(int l,int r){return query(r)-query(l-1);}
set<int> s[MAXN];
vector<int> del;
void build(int k,int l,int r)
{
if(l==r) {mx[k]=pre[l];return ;}int mid=l+r>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
void upd(int k,int l,int r,int x)
{
if(l==r) {mx[k]=pre[x];return ;}int mid=l+r>>1;
x<=mid?upd(k<<1,l,mid,x):upd(k<<1|1,mid+1,r,x);
mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
int xres;
void find(int k,int l,int r,int a,int b,int w)
{
if(a==n+1) return ;
if(a<=l&&r<=b)
{
if(mx[k]<w) return ;
if(l==r) {xres=min(l,xres);return ;}
int mid=l+r>>1;
if(mx[k<<1]>=w) find(k<<1,l,mid,a,b,w);
else find(k<<1|1,mid+1,r,a,b,w);
return ;
}
int mid=l+r>>1;
if(a<=mid) find(k<<1,l,mid,a,b,w);
if(b>mid) find(k<<1|1,mid+1,r,a,b,w);
}
int main()
{
n=read(),m=read();int c,a,b,l,r;
set<int>::iterator it,it2;
rep(i,1,n)
col[i]=read(),w[i]=read(),mdf(i,w[i]),pre[i]=pos[col[i]],pos[col[i]]=i,s[col[i]].insert(i);
build(1,1,n);
rep(i,1,m)
{
c=read(),a=read(),b=read();
if(c==2)
{
l=a;xres=n+1;find(1,1,n,a+1,n,l);r=xres;
ans=getsum(l,r-1);
while(r!=n+1&&b--)
{
xres=n+1;find(1,1,n,r+1,n,l);
if(!mxc[col[r]]) mxc[col[r]]=w[pre[r]],del.pb(col[r]);
ans-=mxc[col[r]];
mxc[col[r]]=max(mxc[col[r]],w[r]);
ans+=mxc[col[r]];
ans+=getsum(r+1,xres-1);
r=xres;
}
for(auto v:del) mxc[v]=0;del.clear();
printf("%lld
",ans);
continue;
}
c=read();swap(b,c);
it=s[col[a]].upper_bound(a);
if(it!=s[col[a]].end())
{
it2=it,it2--;
if(it2!=s[col[a]].begin()) it2--,pre[*it]=*it2;
else pre[*it]=0;
upd(1,1,n,*it);
}
s[col[a]].erase(a);col[a]=c;
mdf(a,-w[a]);w[a]=b;mdf(a,w[a]);
s[c].insert(a);
it=s[c].upper_bound(a);
if(it!=s[c].end()) {pre[*it]=a;upd(1,1,n,*it);}
it--;
if(it!=s[c].begin()) it--,pre[a]=*it;
else pre[a]=0;
upd(1,1,n,a);
}
}