【前言】
刚刚从雅礼集训回来,老师免了期末考。
同时看着一批dalao去WC签约,而我只能在这里恢复训练。
【题目】
BZOJ
luogu
设 f i , j f_{i,j} fi,j表示第 i i i秒在第 j j j个城市的方案数,写出转移方程用矩乘优化即可。
当然还要记录一个计数器。
复杂度 O ( n 3 log t ) O(n^3\log t) O(n3logt)
#include
using namespace std;
const int N=33,mod=2017;
int n,m,T;
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
struct Matrix
{
int a[N][N];
void clear(){memset(a,0,sizeof(a));}
void one(){clear();for(int i=1;i<=n+1;++i)a[i][i]=1;}
//void print(){for(int i=1;i<=n+1;++i,puts(""))for(int j=1;j<=n+1;++j)printf("%d ",a[i][j]);puts("");}
}tran,ans;
Matrix operator *(const Matrix&x,const Matrix&y)
{
Matrix res;res.clear();
for(int i=1;i<=n+1;++i) for(int j=1;j<=n+1;++j) for(int k=1;k<=n+1;++k)
res.a[i][j]+=x.a[i][k]*y.a[k][j];
for(int i=1;i<=n+1;++i) for(int j=1;j<=n+1;++j) res.a[i][j]%=mod;
return res;
}
Matrix qpow(Matrix x,int y)
{
Matrix res;res.one();
for(;y;y>>=1,x=x*x) if(y&1) res=res*x;
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4887.in","r",stdin);
freopen("BZOJ4887.out","w",stdout);
#endif
n=read();m=read();
for(int i=1;i<=m;++i)
{
int u=read(),v=read();
tran.a[u][v]=tran.a[v][u]=1;
}
for(int i=1;i<=n+1;++i) tran.a[i][i]=tran.a[i][n+1]=1;
T=read();ans.a[1][1]=1;ans=ans*qpow(tran,T+1);
printf("%d\n",ans.a[1][n+1]);
return 0;
}
对于位运算题一般按位考虑答案,那么就是求所有和每一位出现 0 0 0的个数。
显然区间和可以转化为前缀差分。
那么对于 s i − s j , j < i s_i-s_j,j<i si−sj,j<i,我们对于每一位考虑时,可以分类讨论这一位结果为 0 0 0或 1 1 1的充要条件。观察到结果只和低位组成的数字大小有关,而数字和不超过 1 0 6 10^6 106,于是我们对 0 , 1 0,1 0,1分别维护一个 BIT \text{BIT} BIT记录前缀数字出现次数来分类讨论即可。
这样做复杂度是 O ( n log 2 c ) O(n\log^2 c) O(nlog2c)的,其中 c c c是所有数的和。
我们还可以多项式学傻了系列:
我们考虑每种权值的出现次数,令 f ( x ) f(x) f(x)表示前缀和的生成函数,那么系数 f i ∗ f j → f i − j f_i*f_j\rightarrow f_{i-j} fi∗fj→fi−j。
于是我们将 f ( x ) f(x) f(x)和它倒过来做一个卷积即可。
这样做复杂度是 O ( c log c ) O(c\log c) O(clogc)的。
当然跑不过去
#include
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,ans,a[N],fc[22];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
struct BIT
{
int c[N];
void clear(){memset(c,0,sizeof(c));}
void update(int x){for(;x<N;x+=lowbit(x))c[x]++;}
int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
int qsum(int l,int r){if(l>r)return 0;return query(r)-query(l-1);}
}T0,T1;
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4888.in","r",stdin);
freopen("BZOJ4888.out","w",stdout);
#endif
n=read();
for(int i=1;i<=n;++i) a[i]=read()+a[i-1];
fc[0]=1;for(int i=1;i<20;++i) fc[i]=fc[i-1]<<1;
for(int k=0;k<20;++k)
{
T0.clear();T1.clear();ll res=0;
for(int i=0;i<=n;++i)
{
int tmp=a[i]%fc[k]+1;
if(a[i]&fc[k])
{
res+=T0.qsum(1,tmp)+T1.qsum(tmp+1,N-1);
T1.update(tmp);
}
else
{
res+=T0.qsum(tmp+1,N-1)+T1.qsum(1,tmp);
T0.update(tmp);
}
}
ans+=(res&1)*fc[k];
}
printf("%d",ans);
return 0;
}
#include
using namespace std;
typedef double db;
const int N=2097154;
const db pi=acos(-1);
int n,ans,s[N];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
namespace FFT
{
int m,L,rev[N];
struct cd
{
db r,i;
cd(db _r=0,db _i=0):r(_r),i(_i){}
cd operator +(const cd&a)const{return cd(r+a.r,i+a.i);}
cd operator -(const cd&a)const{return cd(r-a.r,i-a.i);}
cd operator *(const cd&a)const{return cd(r*a.r-i*a.i,r*a.i+i*a.r);}
}a[N],b[N];
void reget(int n)
{
for(m=1,L=0;m<=n*2;m<<=1,++L);
for(int i=0;i<m;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
}
void fft(cd *a,int n,int f)
{
for(int i=0;i<n;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1)
{
cd wn=cd(cos(pi/i),f*sin(pi/i));
for(int j=0;j<n;j+=i<<1)
{
cd w=cd(1,0);
for(int k=0;k<i;++k,w=w*wn)
{
cd x=a[j+k],y=w*a[i+j+k];
a[j+k]=x+y;a[i+j+k]=x-y;
}
}
}
if(!~f)for(int i=0;i<n;++i)a[i].r/=n;
}
}
using namespace FFT;
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4888.in","r",stdin);
freopen("BZOJ4888.out","w",stdout);
#endif
n=read();a[0].r=1;
for(int i=1;i<=n;++i) s[i]=read()+s[i-1],++a[s[i]].r;
for(int i=0;i<=s[n];++i) b[s[n]-i].r=a[i].r;
reget(s[n]);fft(a,m,1);fft(b,m,1);
for(int i=0;i<m;++i) a[i]=a[i]*b[i];
fft(a,m,-1);
for(int i=0;i<=s[n];++i) if((int)(a[i].r+0.5)&1) ans^=(s[n]-i);
printf("%d\n",ans);
return 0;
}
对于一次交换产生的影响只是在这个区间内部。
考虑交换的影响,就是区间中比 l l l的 a l a_l al大的所有数都会产生 v l v_l vl的贡献,比 a l a_l al小的所有数都会产生 − v l -v_l −vl的贡献。 r r r类似考虑。
这样就是一个查询一个区间内大于某个数的个数及其权值和的操作。
直接上树套树就行了,第一维区间 BIT \text{BIT} BIT,第二维权值线段树。
复杂度 O ( n log 2 n ) O(n\log^2 n) O(nlog2n)
#include
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;
const int N=5e4+10,M=N*200,mod=1e9+7;
int n,m,a[N],b[N];
ll ans;
namespace IO
{
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10^48);
}
void writeln(int x){write(x);putchar('\n');}
}
ll upm(ll x){x%=mod;if(x<0)x+=mod;return x;}
void up(ll &x,ll y){x=upm(x+y);}
namespace Tree_up_Tree
{
int rt[N];
struct node
{
ll val;int cnt,ls,rs;
node(ll _val=0,int _cnt=0,int _ls=0,int _rs=0):val(_val),cnt(_cnt),ls(_ls),rs(_rs){}
};
struct Segment
{
node t[M];int sz;
void update(int &x,int l,int r,int p,int vl,int ct)
{
if(!x) x=++sz; t[x].val+=vl;t[x].cnt+=ct;
if(l==r) return;
int mid=(l+r)>>1;
if(p<=mid) update(t[x].ls,l,mid,p,vl,ct);
else update(t[x].rs,mid+1,r,p,vl,ct);
}
ll queryv(int x,int l,int r,int L,int R)
{
if(!x) return 0;
if(L<=l && r<=R) return t[x].val;
int mid=(l+r)>>1;ll res=0;
if(L<=mid) res+=queryv(t[x].ls,l,mid,L,R);
if(R>mid) res+=queryv(t[x].rs,mid+1,r,L,R);
return res;
}
int queryc(int x,int l,int r,int L,int R)
{
if(!x) return 0;
if(L<=l && r<=R) return t[x].cnt;
int mid=(l+r)>>1,res=0;
if(L<=mid) res+=queryc(t[x].ls,l,mid,L,R);
if(R>mid) res+=queryc(t[x].rs,mid+1,r,L,R);
return res;
}
}tr;
struct BIT
{
void update(int x,int p,int vl,int ct){for(;x<=n;x+=lowbit(x))tr.update(rt[x],1,n,p,vl,ct);}
ll queryv(int l,int r,int L,int R)
{
if(l>r || L>R) return 0;
ll res=0;
for(;r;r-=lowbit(r)) res+=tr.queryv(rt[r],1,n,L,R);
for(--l;l;l-=lowbit(l)) res-=tr.queryv(rt[l],1,n,L,R);
return res;
}
int queryc(int l,int r,int L,int R)
{
if(l>r || L>R) return 0;
int res=0;
for(;r;r-=lowbit(r)) res+=tr.queryc(rt[r],1,n,L,R);
for(--l;l;l-=lowbit(l)) res-=tr.queryc(rt[l],1,n,L,R);
return res;
}
}bit;
void solve()
{
for(int i=1;i<=n;++i) bit.update(i,a[i],b[i],1);
while(m--)
{
int l=IO::read(),r=IO::read();if(l>r) swap(l,r);
if(l==r) {IO::writeln(ans);continue;}
up(ans,bit.queryv(l+1,r-1,1,a[r]-1));
up(ans,(ll)bit.queryc(l+1,r-1,1,a[r]-1)*b[r]);
up(ans,-bit.queryv(l+1,r-1,a[r]+1,n));
up(ans,(ll)-bit.queryc(l+1,r-1,a[r]+1,n)*b[r]);
up(ans,bit.queryv(l+1,r-1,a[l]+1,n));
up(ans,(ll)bit.queryc(l+1,r-1,a[l]+1,n)*b[l]);
up(ans,-bit.queryv(l+1,r-1,1,a[l]-1));
up(ans,(ll)-bit.queryc(l+1,r-1,1,a[l]-1)*b[l]);
up(ans,(ll)(a[l]<a[r]?1:-1)*(b[l]+b[r]));
bit.update(l,a[l],-b[l],-1);bit.update(l,a[r],b[r],1);
bit.update(r,a[r],-b[r],-1);bit.update(r,a[l],b[l],1);
swap(a[l],a[r]);swap(b[l],b[r]);
IO::writeln(ans);
}
}
}
namespace BIT
{
struct BIT
{
ll val[N];int cnt[N];
void update(int x,int v){for(;x<=n;x+=lowbit(x))val[x]+=v,++cnt[x];}
ll queryc(int x){ll res=0;for(;x;x-=lowbit(x))res+=cnt[x];return res;}
ll queryv(int x){ll res=0;for(;x;x-=lowbit(x))res+=val[x];return res;}
}bit;
void initans()
{
for(int i=n;i;--i)
bit.update(a[i],b[i]),up(ans,bit.queryv(a[i]-1)+(ll)bit.queryc(a[i]-1)*b[i]);
}
}
void init()
{
n=IO::read();m=IO::read();
for(int i=1;i<=n;++i) a[i]=IO::read(),b[i]=IO::read();
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4889.in","r",stdin);
freopen("BZOJ4889.out","w",stdout);
#endif
init();
BIT::initans();
Tree_up_Tree::solve();
return 0;
}
枚举删去哪条边,求出两棵树的带权重心,那么最优加边一定是在两个带权重心之间,再和两棵树的直径作比较。选取所有情况下最优值。复杂度 O ( n 2 ) O(n^2) O(n2)
#include
using namespace std;
const int N=5005,INF=1e9;
int n,ans,tot,ban,rt1,rt2;
int head[N],fr[N],dis[N];
queue<int>q;
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
struct Tway{int v,w,nex;}e[N<<1];
void add(int u,int v,int w)
{
e[++tot]=(Tway){v,w,head[u]};head[u]=tot;
e[++tot]=(Tway){u,w,head[v]};head[v]=tot;
}
void bfs(int &rt)
{
q.push(rt);fr[rt]=dis[rt]=0;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(~dis[v] || i==ban || i==(ban^1)) continue;
dis[v]=dis[x]+e[i].w;fr[v]=x;q.push(v);
if(dis[v]>dis[rt]) rt=v;
}
}
}
int getlen(int x)
{
int rt=x,res=INF;
for(;x;x=fr[x]) res=min(res,max(dis[x],dis[rt]-dis[x]));
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4890.in","r",stdin);
freopen("BZOJ4890.out","w",stdout);
#endif
ans=INF;tot=1;n=read();
for(int i=1,u,v,w;i<n;++i) u=read(),v=read(),w=read(),add(u,v,w);
for(int i=2;i<n*2;i+=2)
{
ban=i;rt1=e[i].v;rt2=e[i^1].v;
//printf("cut:%d %d %d\n",rt1,rt2,e[i].w);
memset(dis,-1,sizeof(dis));bfs(rt1);bfs(rt2);
memset(dis,-1,sizeof(dis));bfs(rt1);bfs(rt2);
//printf("rt:%d %d dis:%d %d\n",rt1,rt2,dis[rt1],dis[rt2]);
int tmpans=max(dis[rt1],dis[rt2]);
int len1=getlen(rt1),len2=getlen(rt2);
//printf("half:%d %d\n",len1,len2);
tmpans=max(tmpans,len1+len2+e[i].w);
ans=min(ans,tmpans);
}
printf("%d\n",ans);
return 0;
}
一种显然的暴力是分解质因数后约分,再判断分母是否与模数互质。
这样做的复杂度是 O ( m ( M + log M ) ) O(m(\sqrt M +\log M)) O(m(M+logM))的(这里 a i a_i ai与 M M M同阶)。
考虑瓶颈在分解质因数,于是我们只需要写一个 pollard-rho \text{pollard-rho} pollard-rho就可以过了。
最后的复杂度就是 O ( m ( M 1 4 + log M ) ) O(m(M^{\frac 1 4}+\log M)) O(m(M41+logM))。
需要用到快速乘,一定要用long double,不然就凉了。
#include
using namespace std;
typedef long double ldb;
typedef long long ll;
const ll N=1e4+10;
const ldb eps=1e-8;
ll pnum,n,m,K;
ll a[22][N],b[N],pri[N],num0[N],num[N];
ll read()
{
ll ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
namespace Divide_Prime
{
ll mul(ll x,ll y,ll mod){return ((x*y-(ll)((ldb)x/mod*y+eps)*mod)%mod+mod)%mod;}
ll qpow(ll x,ll y,ll mod)
{
ll res=1;
for(;y;y>>=1,x=mul(x,x,mod))if(y&1)res=mul(res,x,mod);
return res;
}
bool checkpri(ll a,ll y,int cnt,ll mod)
{
ll x=qpow(a,y,mod);if(x==1 || x==mod-1) return 1;
for(int i=1;i<=cnt;++i)
{
x=mul(x,x,mod);
if(x==1) return 0;
if(x==mod-1) return 1;
}
return 0;
}
bool miller_rabin(ll x)
{
if(x==2) return 1;
if(x%2==0) return 0;
ll y=x-1;int cnt=0;
while(y%2==0) y>>=1,++cnt;
for(int i=1;i<=5;++i) if(!checkpri(rand()%(x-2)+1,y,cnt,x)) return 0;
return 1;
}
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
ll pollard_rho(ll a,ll mod)
{
ll x=rand()%mod,y=x,k=2,g=1;
for(int i=1;g==1;++i)
{
x=(mul(x,x,mod)+a)%mod;
g=gcd(abs(x-y),mod);
if(i==k){k<<=1;y=x;}
}
return g;
}
void divide(ll x)
{
//cerr<
if(x==1) return;
if(miller_rabin(x)){pri[++pnum]=x;return;}
ll t=x;while(t==x)t=pollard_rho(rand()%(x-1)+1,x);
divide(t);divide(x/t);
}
}
using namespace Divide_Prime;
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4891.in","r",stdin);
freopen("BZOJ4891.out","w",stdout);
#endif
srand(19260817);
n=read();m=read();K=read();
for(int i=1;i<=m;++i) b[i]=read();
for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) a[i][j]=read();
while(K--)
{
ll id=read(),mod=read();pnum=0;divide(mod);
sort(pri+1,pri+pnum+1);
//for(int i=1;i<=pnum;++i) cerr<
ll cnt=0,phi=mod;
for(int i=1;i<=pnum;++i)
{
if(pri[i]^pri[i-1]) pri[++cnt]=pri[i],phi-=phi/pri[i],num0[cnt]=num[cnt]=0;
++num0[cnt];
}
ll ans=1,inv=1;
for(int i=1;i<=m;++i)
{
ll x=b[i];
for(int j=1;j<=cnt;++j) while(!(x%pri[j])) x/=pri[j],++num[j];
ans=mul(ans,x,mod);
}
for(int i=1;i<=m;++i)
{
ll x=a[id][i];
for(int j=1;j<=cnt;++j) while(!(x%pri[j])) x/=pri[j],--num[j];
inv=mul(inv,x,mod);
}
bool fg=1;
for(int i=1;i<=cnt;++i) if(num[i]<0){puts("-1");fg=0;break;}
if(!fg) continue;
for(int i=1;i<=cnt;++i) if(num[i]) ans=mul(ans,qpow(pri[i],num[i],mod),mod);
ans=mul(ans,qpow(inv,phi-1,mod),mod);
printf("%lld\n",ans);
}
return 0;
}
枚举开头位置暴力 L C P LCP LCP即可。
顺便研究了一下后缀数组的板子。
复杂度 O ( T n log n ) O(Tn\log n) O(Tnlogn)
或者这个东西相当于求每一位开始匹配有多少位不同,显然可以用 FFT \text{FFT} FFT来做,复杂度是一样的。
可是还是跑不过
#include
using namespace std;
const int N=2e5+10;
int T,a[N];
char s[N],t[N];
namespace SA
{
int sa[N],rk[N],hi[N],h[20][N];
int wa[N],wb[N],wx[N],wy[N];
int remn,Log[N],fc[20];
bool cmp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}
void getsa(int *r,int n,int m)
{
int *x=wa,*y=wb,*t,i,j,p;
for(i=0;i<m;++i) wx[i]=0;
for(i=0;i<n;++i) wx[x[i]=r[i]]++;
for(i=1;i<m;++i) wx[i]+=wx[i-1];
for(i=n-1;~i;--i) sa[--wx[x[i]]]=i;
for(j=1,p=0;p<n;j<<=1,m=p)
{
for(p=0,i=n-j;i<n;++i) y[p++]=i;
for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<m;++i) wx[i]=0;
for(i=0;i<n;++i) wx[wy[i]=x[y[i]]]++;
for(i=1;i<m;++i) wx[i]+=wx[i-1];
for(i=n-1;~i;--i) sa[--wx[wy[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;++i)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
}
void getheight(int *r,int n)
{
int i,j,k=0;
for(i=1;i<=n;++i) rk[sa[i]]=i;
for(i=0;i<n;hi[rk[i++]]=k)
for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];++k);
}
void adjust(int n)
{
for(int i=1;i<=n;++i) sa[i]++;
for(int i=n;i;--i) rk[i]=rk[i-1];
sa[0]=rk[0]=0;
}
void initrmq(int n)
{
for(int i=1;i<=n;++i) h[0][i]=hi[i];
for(int j=1;j<20;++j) for(int i=1;i+fc[j]-1<=n;++i)
h[j][i]=min(h[j-1][i],h[j-1][i+fc[j-1]]);
}
void initsa(int *r,int n)
{
remn=n;
getsa(r,n+1,30);getheight(r,n);adjust(n);initrmq(n);
}
int calc(int l,int r){int t=Log[r-l+1];return min(h[t][l],h[t][r-fc[t]+1]);}
int lcp(int x,int y){x=rk[x];y=rk[y];if(x>y)swap(x,y);return calc(x+1,y);}
void clear(){memset(sa,0,(remn+2)<<2);memset(rk,0,(remn+2)<<2);memset(hi,0,(remn+2)<<2);}
void initLog()
{
Log[1]=0;for(int i=2;i<N;++i)Log[i]=Log[i>>1]+1;
fc[0]=1;for(int i=1;i<20;++i) fc[i]=fc[i-1]<<1;
}
}
using SA::lcp;
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ4892.in","r",stdin);
freopen("BZOJ4892.out","w",stdout);
#endif
scanf("%d",&T);SA::initLog();
while(T--)
{
scanf("%s%s",s,t);
int tn=strlen(s),n=tn,len=strlen(t);
if(n<len){puts("0");continue;}
s[n++]='Z'+1;
for(int i=0;i<len;++i) s[n++]=t[i];
for(int i=0;i<n;++i) a[i]=s[i]-'A'+1;
SA::clear();SA::initsa(a,n);//SA::output();
int ans=0;
for(int i=1;i+len-1<=tn;++i)
{
//printf("beg:%d\n",i);
int now=tn+lcp(i,tn+2)+1,k;
if(now==n) {++ans;continue;}
//printf("%d\n",now);
for(k=1;k<=3;++k)
{
now+=2;
if(now>n) {++ans;break;}
//printf("cmp:%d %d ",now-tn-1+i-1,now);
now=now+lcp(now-tn-1+i-1,now)-1;
//printf("goto:%d \n",now);
if(now==n) {++ans;break;}
}
//puts("");
}
printf("%d\n",ans);
}
//cerr<
return 0;
}