[SDOI2016Round1]解题报告
Posted yutingliuyl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2016Round1]解题报告相关的知识,希望对你有一定的参考价值。
Day1
T1:
题意:求
由于是抑或操作。每一位都是独立的,所以能够一位一位的算贡献。
复杂度是
#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。假设
配对关系是一个二分图,所以建出费用流的图来,当跑到费用小于0的时候,处理一下当前的流量。退出即可了。
#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:
题意:给出一棵树,有点权和边权,有两种操作,一种是将一条路径上的点权改动成
第一种改动能够看成是往树上的一段区间中增加一个线段。这个东西能够用线段树维护。用线段树维护这个区间的线段的斜率和截距。每次向这个区间中增加一条新的线段的时候。看一下这段区间中原来的线段和新增加的线段哪一个对这段区间的影响更大,将影响小的线段下放到它有影响的子树,每次一直这样下放到叶子节点。这样每次插入的复杂度是
每次查询的时候仅仅须要看一下这段区间中的最小值和这段区间中所保存的线段的两个端点的情况即可了,复杂度是
这道题每次的dis数组还是不知道的。能够在增加线段的时候顺便将lca传进去(常数大了好多。
。。)
再加上链剖,总的复杂度就是
#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:
题意:每次向一个字符串的结尾加一个字符,输出当前有多少种不同的非空子串。
先将这个字符串全读进来,然后把字符串反过来建后缀数组。总的答案就是
由于要动态的查询。所以维护一个rank的权值线段树,每次增加一个之后查询一下前驱和后继。每次更新的答案就是:
#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个数满足
答案就是
错拍的公式就是:
求c的时候预处理一下阶乘。
#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份的最小的方差是多少。答案要求
把
#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]));
}
以上是关于[SDOI2016Round1]解题报告的主要内容,如果未能解决你的问题,请参考以下文章
Cogs 2221. [SDOI2016 Round1] 数字配对
NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记