Day1
T1:
题意:求∑n?1i=0∑m?1j=0max((i xor j)?k,0)
因为是抑或操作,每一位都是独立的,所以可以一位一位的算贡献。
f[i][a][b][c]表示第i位时,每个数跟n,m,k的大小关系,0表示小于,1表示i位之前都相等,2表示大于。转移的时候美剧当前这一位的数是什么,从高位向低位转移就行了。
复杂度是O(nlogn)
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int N=100;
int T,Mod,ni[N],mi[N],ki[N],len;
LL n,m,K,f[N][2][2][3],g[N][2][2][3];
inline void calc(){
int i,a,b,c,o0,o1,x,y,z,o;
g[0][1][1][1]=1;
for(i=0;i<len;++i)
for(a=0;a<=1;++a)
for(b=0;b<=1;++b)
for(c=0;c<=2;++c)
if(f[i][a][b][c]+g[i][a][b][c])
for(o0=0;o0<=1;++o0){
if(a){
if(o0>ni[i+1]) continue;
if(o0==ni[i+1]) x=1;
if(o0<ni[i+1]) x=0;
}
else x=0;
for(o1=0;o1<=1;++o1){
if(b){
if(o1>mi[i+1]) continue;
if(o1==mi[i+1]) y=1;
if(o1<mi[i+1]) y=0;
}
else y=0;
o=o0^o1;
if(c==0||c==2) z=c;
if(c==1){
if(o>ki[i+1]) z=2;
if(o==ki[i+1]) z=1;
if(o<ki[i+1]) z=0;
}
g[i+1][x][y][z]=(g[i+1][x][y][z]+g[i][a][b][c])%Mod;
f[i+1][x][y][z]=(f[i+1][x][y][z]+f[i][a][b][c]*2LL+g[i][a][b][c]*(LL)o)%Mod;
}
}
}
int main(){
scanf("%d",&T);
while(T--){
LL x;
int i;
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(ni,0,sizeof(ni));
memset(mi,0,sizeof(mi));
memset(ki,0,sizeof(ki));
scanf("%lld%lld%lld%d",&n,&m,&K,&Mod);
for(x=n;x;x>>=1) ni[++ni[0]]=x&1LL;
for(x=m;x;x>>=1) mi[++mi[0]]=x&1LL;
for(x=K;x;x>>=1) ki[++ki[0]]=x&1LL;
len=max(ni[0],max(mi[0],ki[0]));
for(i=1;i<=(len>>1);++i){
swap(ni[i],ni[len-i+1]);
swap(mi[i],mi[len-i+1]);
swap(ki[i],ki[len-i+1]);
}
calc();
printf("%lld\n",(f[len][0][0][2]-(K%Mod*g[len][0][0][2])%Mod+Mod)%Mod);
}
}
T2:
题意:给出n种数字,第i个是ai,个数是bi,权值是ci。如果ai|aj而且aiaj是一个质数,那么这两个数字就能获得ci?cj的价值,要求总价值不小于0的情况下求最多的配对数。
配对关系是一个二分图,所以建出费用流的图来,当跑到费用小于0的时候,处理一下当前的流量,退出就行了。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define T n+2
#define D 795
#define LL long long
#define inf 1000000000
#define INF 10000000000000LL
const int N=800;
const int M=100000;
const int O=2000000;
bool flag[M+10],use[N],f[N];
struct S{int st,en,va;LL co;}aa[O];
int n,a[N],b[N],c[N],prime[M+10],d[N],va,point[N],pre[N],next[O],map[N][N],tot=1,co[N];
int l[N],to[N];
LL dis[N];
inline void prepare(){
int i,j;
for(i=2;i<=M;++i){
if(!flag[i]) prime[++prime[0]]=i;
for(j=1;j<=prime[0]&&i*prime[j]<=M;++j){
flag[i*prime[j]]=true;
if(i%prime[j]==0) break;
}
}
}
inline void add(int x,int y,int z,LL co){
next[++tot]=point[x];point[x]=tot;
aa[tot].st=x;aa[tot].en=y;aa[tot].va=z;aa[tot].co=co;
next[++tot]=point[y];point[y]=tot;
aa[tot].st=y;aa[tot].en=x;aa[tot].va=0;aa[tot].co=-co;
}
inline bool check(int x,int y){
int i,j,now,num=0;
if((a[x]%a[y])||(a[x]==a[y])) return false;
now=a[x]/a[y];
for(i=1;i<=prime[0];++i){
while(now%prime[i]==0) ++num,now/=prime[i];
if(num>1) return false;
if(now==1) return true;
}
if(now!=1) ++num;
return num==1;
}
inline void paint(int x,int kind){
int i;
co[x]=kind;
for(i=1;i<=n;++i)
if(map[x][i]&&!co[i])
paint(i,kind==1?2:1);
}
inline LL SPFA(int x,int y){
int h=0,t=1,i,u;
l[t]=x;
for(i=1;i<=T;++i) dis[i]=-INF;
dis[x]=0;
while(h!=t){
h=h%D+1;u=l[h];f[u]=true;
for(i=point[u];i;i=next[i])
if(aa[i].va>0&&dis[aa[i].en]<dis[u]+aa[i].co){
dis[aa[i].en]=dis[u]+aa[i].co;
pre[aa[i].en]=i;
if(f[aa[i].en]){
f[aa[i].en]=false;
t=t%D+1;
l[t]=aa[i].en;
}
}
}
return dis[y];
}
inline int ISAP(int x,int y){
int minn=inf,i;
for(i=y;i!=x;i=aa[pre[i]].st)
minn=min(minn,aa[pre[i]].va);
for(i=y;i!=x;i=aa[pre[i]].st){
aa[pre[i]].va-=minn;
aa[pre[i]^1].va+=minn;
}
return minn;
}
int main(){
int i,j,o=0,num=0,maxn=0;
prepare();
scanf("%d",&n);
for(i=1;i<=n;++i) scanf("%d",&a[i]);
for(i=1;i<=n;++i) scanf("%d",&b[i]);
for(i=1;i<=n;++i) scanf("%d",&c[i]),o+=(c[i]==0);
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
if(check(i,j)) map[i][j]=map[j][i]=1;
for(i=1;i<=n;++i)
if(!co[i]) paint(i,1);
for(i=1;i<=n;++i)
if(co[i]==1) add(1,i+1,b[i],0);
else add(i+1,T,b[i],0);
for(i=1;i<=n;++i)
for(j=i+1;j<=n;++j)
if(map[i][j]||map[j][i]){
int x=i,y=j;
if(co[x]==2) swap(x,y);
add(x+1,y+1,inf,(LL)c[x]*c[y]);
}
memset(f,1,sizeof(f));
LL minn,ans=0;
while(minn!=-INF){
minn=SPFA(1,T);
if(minn!=-INF){
int now=ISAP(1,T);
ans=ans+minn*(LL)now;
if(ans<0LL){
ans-=minn*(LL)now;
printf("%d\n",num+min(now,(int)(-ans/minn)));
return 0;
}
num+=now;
}
}
printf("%d\n",num);
}
T3:
题意:给出一棵树,有点权和边权,有两种操作,一种是将一条路径上的点权修改成min(v[x],a?dis[x]+b),dis[x] 表示x到起点的距离(只算边权)。另一种是询问a到b路径中最小的点权。
第一种修改可以看成是往树上的一段区间中加入一个线段。这个东西可以用线段树维护。用线段树维护这个区间的线段的斜率和截距。每次向这个区间中加入一条新的线段的时候,看一下这段区间中原来的线段和新加入的线段哪一个对这段区间的影响更大,将影响小的线段下放到它有影响的子树,每次一直这样下放到叶子节点。这样每次插入的复杂度是O(log2n)。
每次查询的时候只需要看一下这段区间中的最小值和这段区间中所保存的线段的两个端点的情况就行了,复杂度是O(logn)。
这道题每次的dis数组还是不知道的,可以在加入线段的时候顺便将lca传进去(常数大了好多。。。)
再加上链剖,总的复杂度就是O(nlog3n)
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mid (l+r)/2
#define L k<<1,l,mid
#define R k<<1|1,mid+1,r
#define LL long long
#define D 123456789123456789LL
const int N=100010;
LL dis[N],a,b;
struct S{int st,en,va;}aa[N<<1];
struct T{int lca,st,kind;LL k,b,minn;}tr[N<<2];
int n,m,point[N],next[N<<1],tot,deep[N],siz[N],son[N],belong[N],fa[N][21],q[N],pos[N],dfsn,cur[N],lca,to[N],flag[N<<2];
inline int in(){
int f=1,x=0;char ch=getchar();
while(ch<‘0‘||ch>‘9‘){
if(ch==‘-‘) f=-1;
ch=getchar();
}
while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
return x*f;
}
inline int IN(){
LL f=1,x=0;char ch=getchar();
while(ch<‘0‘||ch>‘9‘){
if(ch==‘-‘) f=-1;
ch=getchar();
}
while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
return x*f;
}
inline void add(int x,int y,int z){
next[++tot]=point[x];point[x]=tot;
aa[tot].st=x;aa[tot].en=y;aa[tot].va=z;
next[++tot]=point[y];point[y]=tot;
aa[tot].st=y;aa[tot].en=x;aa[tot].va=z;
}
inline void prepare(){
int h=1,t=1,u,i,j;
q[h]=1;
while(h<=t){
u=q[h];
for(i=point[u];i;i=next[i])
if(aa[i].en!=fa[u][0]){
q[++t]=aa[i].en;
deep[aa[i].en]=deep[u]+1;
fa[aa[i].en][0]=u;
dis[aa[i].en]=dis[u]+(LL)aa[i].va;
for(j=1;j<=20;++j)
fa[aa[i].en][j]=fa[fa[aa[i].en][j-1]][j-1];
}
h+=1;
}
for(i=t;i;--i){
u=q[i];siz[u]=1;
for(j=point[u];j;j=next[j])
if(aa[j].en!=fa[u][0]){
siz[u]+=siz[aa[j].en];
if(siz[aa[j].en]>siz[son[u]]) son[u]=aa[j].en;
}
}
int now=1;
for(i=1;i<=n;++i) cur[i]=point[i];
while(now){
if(!pos[now]){
pos[now]=++dfsn;
to[dfsn]=now;
belong[now]=(now==son[fa[now][0]] ? belong[fa[now][0]] : now);
now=(son[now] ? son[now] : fa[now][0]);
}
else{
for(i=cur[now];i&&(aa[i].en==fa[now][0]||aa[i].en==son[now]);i=next[i]);
cur[now]=next[i];
belong[aa[i].en]=aa[i].en;
now=(i ? aa[i].en : fa[now][0]);
}
}
}
inline int LCA(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
int t=deep[x]-deep[y],i;
for(i=0;i<=20;++i)
if(t&(1<<i)) x=fa[x][i];
for(i=20;~i;--i)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return x==y?x:fa[x][0];
}
inline void build(int k,int l,int r){
if(l==r){
tr[k].k=0;
tr[k].kind=-1;
tr[k].lca=to[l];
tr[k].b=tr[k].minn=D;
return ;
}
build(L);build(R);
tr[k]=tr[k<<1];
}
inline int LL calc(int x,int y,int z,int kind){
x=to[x];
if(kind==0) return dis[z]-dis[x];
if(kind==1) return dis[z]-dis[y]+dis[x]-dis[y];
}
inline void insert(int k,int l,int r,int x,int y,T now){
if(x<=l&&y>=r){
LL minn=D;
minn=min(minn,calc(l,now.lca,now.st,now.kind)*now.k+now.b);
minn=min(minn,calc(r,now.lca,now.st,now.kind)*now.k+now.b);
now.minn=tr[k].minn=min(tr[k].minn,minn);
if(!flag[k]){
flag[k]=1;
tr[k]=now;
return ;
}
int o1=calc(l,now.lca,now.st,now.kind)*now.k+now.b<calc(l,tr[k].lca,tr[k].st,tr[k].kind)*tr[k].k+tr[k].b;
if(l==r){
if(o1) tr[k]=now;
return ;
}
int o2=calc(mid,now.lca,now.st,now.kind)*now.k+now.b<calc(mid,tr[k].lca,tr[k].st,tr[k].kind)*tr[k].k+tr[k].b;
int o3=calc(r,now.lca,now.st,now.kind)*now.k+now.b<calc(r,tr[k].lca,tr[k].st,tr[k].kind)*tr[k].k+tr[k].b;
if(o1&&o3){
tr[k]=now;
return ;
}
if(!o1&&!o3) return;
if(o2){
swap(tr[k],now);
o1^=1;o3^=1;
}
if(o1) insert(L,x,y,now);
if(o3) insert(R,x,y,now);
return ;
}
if(x<=mid) insert(L,x,y,now);
if(y>mid) insert(R,x,y,now);
tr[k].minn=min(tr[k].minn,min(tr[k<<1].minn,tr[k<<1|1].minn));
}
inline LL query(int k,int l,int r,int x,int y){
LL minn=D;
if(x<=l&y>=r){
LL o_l=calc(l,tr[k].lca,tr[k].st,tr[k].kind);
LL o_r=calc(r,tr[k].lca,tr[k].st,tr[k].kind);
return min(tr[k].minn,min(o_l*tr[k].k+tr[k].b,o_r*tr[k].k+tr[k].b));
}
minn=min(minn,calc(max(x,l),tr[k].lca,tr[k].st,tr[k].kind)*tr[k].k+tr[k].b);
minn=min(minn,calc(min(y,r),tr[k].lca,tr[k].st,tr[k].kind)*tr[k].k+tr[k].b);
if(x<=mid) minn=min(minn,query(L,x,y));
if(y>mid) minn=min(minn,query(R,x,y));
return minn;
}
inline void change(int x,int y,int st,int kind){
int i,j;
T now=(T){lca,st,kind,a,b,D};
while(belong[x]!=belong[y]){
insert(1,1,n,pos[belong[x]],pos[x],now);
x=fa[belong[x]][0];
}
insert(1,1,n,pos[y],pos[x],now);
}
inline LL ask(int x,int y){
LL minn=D;
while(belong[x]!=belong[y]){
minn=min(minn,query(1,1,n,pos[belong[x]],pos[x]));
x=fa[belong[x]][0];
}
minn=min(minn,query(1,1,n,pos[y],pos[x]));
return minn;
}
int main(){
int i,j,x,y,t,z;
n=in();m=in();
for(i=1;i<n;++i){
x=in();y=in();z=in();
add(x,y,z);
}
prepare();
build(1,1,n);
while(m--){
t=in();x=in();y=in();
lca=LCA(x,y);
if(t==1){
a=IN();b=IN();
change(x,lca,x,0);
change(y,lca,x,1);
}
if(t==2) printf("%lld\n",min(ask(x,lca),ask(y,lca)));
}
}
Day2:
T1:
题意:每次向一个字符串的结尾加一个字符,输出当前有多少种不同的非空子串。
先将这个字符串全读进来,然后把字符串反过来建后缀数组。总的答案就是n?(n+1)2?∑n?1i=1height[i]
因为要动态的查询,所以维护一个rank的权值线段树,每次加入一个之后查询一下前驱和后继,每次更新的答案就是:n?i?lcp(now,pre)?lcp(now,sub)+lcp(pre,sub)
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define inf 0x7fffffff
const int N=100010;
struct S{int minn,maxn;}tr[N<<2];
int n,a[N],b[N],m,t1[N],t2[N],sa[N],rank[N],c[N],height[N],st[N][21],Log[N];
inline int in(){
int x=0;char ch=getchar();
while(ch<‘0‘||ch>‘9‘) ch=getchar();
while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
return x;
}
inline bool cmp(int *y,int p,int q,int k){
int o0,o1;
o0=(p+k>=n)?-1:y[p+k];
o1=(q+k>=n)?-1:y[q+k];
return o0==o1&&y[p]==y[q];
}
inline void build_sa(){
int i,k,p,*x=t1,*y=t2;
for(i=0;i<m;++i) c[i]=0;
for(i=0;i<n;++i) ++c[x[i]=a[i]];
for(i=1;i<m;++i) c[i]+=c[i-1];
for(i=n-1;~i;--i) sa[--c[x[i]]]=i;
for(k=1;k<=n;k<<=1){
for(p=0,i=n-k;i<n;++i) y[p++]=i;
for(i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=0;i<m;++i) c[i]=0;
for(i=0;i<n;++i) ++c[x[y[i]]];
for(i=1;i<m;++i) c[i]+=c[i-1];
for(i=n-1;~i;--i) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
x[sa[0]]=0;m=1;
for(i=1;i<n;++i) x[sa[i]]=(cmp(y,sa[i],sa[i-1],k)?m-1:m++);
if(m>=n) break;
}
}
inline void build_height(){
int i,j,k=0;
for(i=0;i<n;++i) rank[sa[i]]=i;
for(i=0;i<n;++i){
k=k?--k:k;
j=sa[rank[i]-1];
while(a[i+k]==a[j+k]) ++k;
height[rank[i]]=k;
}
/*------------st------------*/
memset(st,127/3,sizeof(st));
for(i=0;i<n;++i) st[i][0]=height[i];
for(j=1;j<=20;++j)
for(i=0;i+(1<<(j-1))<n;++i)
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
for(j=0,i=1;i<=n;++i){
if((1<<(j+1))<=i) ++j;
Log[i]=j;
}
}
inline int LCP(int x,int y){
int k=Log[y-x];++x;
return min(st[x][k],st[y-(1<<k)+1][k]);
}
#define mid (l+r)/2
#define L k<<1,l,mid
#define R k<<1|1,mid+1,r
inline S update(S x,S y){
S ans;
ans.maxn=max(x.maxn,y.maxn);
ans.minn=min(x.minn,y.minn);
return ans;
}
inline void build_tree(int k,int l,int r){
if(l==r){
tr[k].minn=inf;
tr[k].maxn=-inf;
return ;
}
build_tree(L);build_tree(R);
tr[k]=update(tr[k<<1],tr[k<<1|1]);
}
inline void insert(int k,int l,int r,int x){
if(l==r){
tr[k].maxn=tr[k].minn=l;
return ;
}
if(x<=mid) insert(L,x);
else insert(R,x);
tr[k]=update(tr[k<<1],tr[k<<1|1]);
}
inline S query(int k,int l,int r,int x,int y){
S ans1,ans2;
bool f1=false,f2=false;
if(x<=l&&y>=r) return tr[k];
if(x<=mid) ans1=query(L,x,y),f1=true;
if(y>mid) ans2=query(R,x,y),f2=true;
if(f1&&f2) return update(ans1,ans2);
else return f1?ans1:ans2;
}
int main(){
int i;
n=in();
for(i=n-1;~i;--i) a[i]=b[i]=in();
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-b-1;
for(i=0;i<n;++i) a[i]=lower_bound(b+1,b+m+1,a[i])-b-1;
build_sa();
build_height();
build_tree(1,0,n-1);
for(LL ans=0,i=n-1;~i;--i){
ans+=(LL)(n-i);
int now=rank[i];
int pre=now?query(1,0,n-1,0,now-1).maxn:-inf;
int sub=(now==n-1)?inf:query(1,0,n-1,now+1,n-1).minn;
if(pre!=-inf) ans-=(LL)LCP(pre,now);
if(sub!=inf) ans-=(LL)LCP(now,sub);
if(pre!=-inf&&sub!=inf) ans+=(LL)LCP(pre,sub);
insert(1,0,n-1,now);
printf("%lld\n",ans);
}
}
T2:
题意:求有多少长度为n的序列,满足1~n只出现过一次,而且恰好有m个数满足a[i]=i。
答案就是C(n,m)?f[n?m]
C(n,m)表示m个满足条件的数怎样选的方案数,再乘上剩下的所有n?m个数都不在多对应的位置上的方案数,也就是错排。
错拍的公式就是:f[i]=(i?1)?(f[i?1]+f[i?2])
求c的时候预处理一下阶乘。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
#define Mod 1000000007LL
const int M=1000000;
LL f[M+10],g[M+10];
int T,n,m;
inline int in(){
int x=0;char ch=getchar();
while(ch<‘0‘||ch>‘9‘) ch=getchar();
while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
return x;
}
inline LL quickpow(LL x,LL y){
LL ans=1;
while(y){
if(y&1LL) ans=(ans*x)%Mod;
y>>=1LL; x=(x*x)%Mod;
}
return ans;
}
int main(){
int i;
T=in();
for(f[1]=0LL,f[2]=1LL,f[0]=1LL,i=3;i<=M;++i) f[i]=(LL)(i-1)*((f[i-1]+f[i-2])%Mod)%Mod;
for(g[1]=g[0]=1LL,i=2;i<=M;++i) g[i]=(g[i-1]*(LL)i)%Mod;
while(T--){
n=in();m=in();
if(m>n){
printf("0\n");
continue;
}
LL ans=f[n-m]*((g[n]*quickpow(g[m],Mod-2)%Mod)*quickpow(g[n-m],Mod-2)%Mod)%Mod;
printf("%lld\n",ans);
}
}
T3:
题意:有一个长度为n的序列,每个节点都有一个权值,分成m份,求m份的最小的方差是多少。答案要求?m2。
ans=m?∑mi=1(xi?xˉ)2
=m?∑mi?1(xi2+xˉ2?2xi?xˉ)
把∑拆开之后,后面两项都是常数,就相当于求每段的平方和的最大。这个东西可以斜率优化。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
const int N=3010;
LL f[N][N],a[N];
int n,m,h[N],t[N],q[N][N];
inline LL pow(LL x){return x*x;}
inline LL get_son(int x,int y,int z){
return f[z][x]+pow(a[x])-f[z][y]-pow(a[y]);
}
inline LL get_fa(int x,int y){
return a[x]-a[y];
}
inline LL calc(int x,int y,int z){
return f[z][x]+pow(a[y]-a[x]);
}
int main(){
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
scanf("%lld",&a[i]),a[i]+=a[i-1];
memset(f,127/3,sizeof(f));
for(j=0;j<=m;++j) f[j][0]=0,q[j][h[j]=t[j]=1]=0;
for(j=1;j<=m;++j)
for(i=1;i<=n;++i){
while(h[j-1]<t[j-1]&&get_son(q[j-1][h[j-1]+1],q[j-1][h[j-1]],j-1)<get_fa(q[j-1][h[j-1]+1],q[j-1][h[j-1]])*(LL)a[i]*2LL) ++h[j-1];
f[j][i]=calc(q[j-1][h[j-1]],i,j-1);
while(h[j]<t[j]&&get_son(i,q[j][t[j]],j)*get_fa(q[j][t[j]],q[j][t[j]-1])<get_son(q[j][t[j]],q[j][t[j]-1],j)*get_fa(i,q[j][t[j]])) --t[j];
q[j][++t[j]]=i;
}
printf("%lld\n",f[m][n]*(LL)m-pow(a[n]));
}