您的位置:首页 > 其它

SDOI2016 Round 1解题报告

2016-04-12 20:52 399 查看
Day1

T1

题目大意:

已知n,m,k,求∑n−1i=0∑m−1j=0max((i xor j)−k,0)

题解:

可以按照数位dp的思想来做,每次考虑在当前这个数的二进制位和n,m这两个二进制位的大小,以及与k的大小,从状态f[i][j][k][l]转移到后继状态,复杂度是O(log(n)∗2∗2∗2∗3)。

但是数位dp毕竟难写难调,我们可以继续考虑,有一棵log(n)层的满二叉Trie,那它的每个叶子节点都可以对应一个0~n−1的数。那我们就可以根据这个想法一部分一部分的来考虑他们对答案的贡献了(左子树−>右子树),比较好写。

Ps. 对于取模中的除法运算,如x=(a/2) mod P可以转化成x=((a mod (2∗P))/2) mod P

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 70
#define LL long long
LL n,m,K,P,ans,mi
;
inline int in(){
int x=0; char ch=getchar(); bool f=1;
while (ch<'0'||ch>'9'){
if (ch=='-') f=0;
ch=getchar();
}while (ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
if (!f) x=-x; return x;
}
inline LL Lin(){
LL x=0; char ch=getchar(); bool f=1;
while (ch<'0'||ch>'9'){
if (ch=='-') f=0;
ch=getchar();
}while (ch>='0'&&ch<='9')
x=x*10ll+(LL)(ch-'0'),ch=getchar();
if (!f) x=-x; return x;
}
inline LL calc(LL x,LL y){
LL s=(((x+y)%(P*2ll))*((y-x+1)%(P*2ll)))%(P*2ll);
s/=2ll,s%=P; return s;
}
inline LL work(LL x,LL y,int xx,int yy){
LL s=0,z; if (xx<yy) swap(x,y),swap(xx,yy);
x^=(x&(mi[xx]-1)),y^=(y&(mi[xx]-1)),z=x^y;
if (z>K) s=calc(z-K,z+mi[xx]-K-1);
else if ((z+mi[xx])>K) s=calc(0,z+mi[xx]-K-1);
s=(s*(mi[yy]%P))%P; return s;
}
int main(){
int T=in(),i,j,tt; LL x,y;
mi[0]=1ll;
for (i=1; i<=62; i++) mi[i]=mi[i-1]<<1;
for (tt=1; tt<=T; tt++){
n=Lin(),m=Lin(),K=Lin();
P=Lin(),x=0ll,ans=0ll;
for (i=62; i>=0; i--){
if (n&mi[i]){
y=0ll;
for (j=62; j>=0; j--){
if (m&mi[j]){
ans=(ans+work(x,y,i,j))%P,y|=mi[j];
}
}x|=mi[i];
}
}printf("%I64d\n",ans);
}return 0;
}


T2

题目大意:

给出n个数,对于两个数x,y,它们能配对的条件是相差一个质因子,配对成功可以获得cx×cy,每个数有bi个可用,求在配对的价值和不小于0的情况下最多能匹配多少对。

题解:

最大费用最大流。题目中给出的是一个二分图,对其建网络流图:

S−>每个奇数个质因子的数,流量为bi,费用为0

每个奇数个质因子的数−>每个能配对的偶数个质因子的数,流量为inf,费用为ci×cj

每个偶数个质因子的数−>T,流量为bi,费用为0

然后跑最大费用流,直到某次增广后总费用为负,计算一下最后跑的流量即可。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 610
#define M 600010
#define P 2000000
#define inf 1000000000
#define limit -100000000000000ll
#define LL long long
struct A{
int x,y,k; LL z;
}a
;
struct e{
int u,v,cap,next; LL cost;
}e[M*2];
int n,ans=0,num=1,S,T,head
,pre
,flow
,q[M*4];
bool vis
; LL dis
,sum=0;
inline int in(){
int x=0; char ch=getchar(); bool f=1;
while (ch<'0'||ch>'9'){
if (ch=='-') f=0;
ch=getchar();
}while (ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
if (!f) x=-x; return x;
}
inline LL Lin(){
LL x=0; char ch=getchar(); bool f=1;
while (ch<'0'||ch>'9'){
if (ch=='-') f=0;
ch=getchar();
}while (ch>='0'&&ch<='9')
x=x*10ll+(LL)(ch-'0'),ch=getchar();
if (!f) x=-x; return x;
}
inline void add(int u,int v,int cap,LL cost){
e[++num].u=u,e[num].v=v,e[num].cap=cap;
e[num].cost=cost,e[num].next=head[u],head[u]=num;
e[++num].u=v,e[num].v=u,e[num].cap=0;
e[num].cost=-cost,e[num].next=head[v],head[v]=num;
}
inline void divide(int xu){
int i,x=a[xu].x,y;
a[xu].k=0,y=sqrt(x);
for (i=2; i<=sqrt(x); i++){
while (!(x%i)) x/=i,a[xu].k++;
}if (x>1) a[xu].k++;
}
inline bool spfa(){
int i,u,v,h=0,t=1;
for (i=S; i<=T; i++)
vis[i]=0,dis[i]=limit;
LL minn=dis[0];
q[h]=S,dis[S]=0ll,flow[S]=inf,vis[S]=1;
while (h<t){
u=q[h%P],h++,vis[u]=0;
for (i=head[u]; i; i=e[i].next){
v=e[i].v;
if (e[i].cap>0&&dis[v]<dis[u]+e[i].cost){
dis[v]=dis[u]+e[i].cost,pre[v]=i;
flow[v]=min(flow[u],e[i].cap);
if (!vis[v]) q[t%P]=v,t++,vis[v]=1;
}
}
}if (dis[T]>minn) return true;
else return false;
}
int main(){
int i,j; n=in(),S=0,T=n+1;
for (i=1; i<=n; i++) a[i].x=in();
for (i=1; i<=n; i++) a[i].y=in();
for (i=1; i<=n; i++) a[i].z=Lin();
for (i=1; i<=n; i++){
divide(i);
if (a[i].k&1) add(S,i,a[i].y,0);
else add(i,T,a[i].y,0);
}for (i=1; i<=n; i++){
if (a[i].k&1){
for (j=1; j<=n; j++){
if (i==j) continue;
if (!(a[j].k&1)){
if (!(a[i].x%a[j].x)&&a[i].k==a[j].k+1)
add(i,j,inf,a[i].z*a[j].z);
if (!(a[j].x%a[i].x)&&a[j].k==a[i].k+1)
add(i,j,inf,a[i].z*a[j].z);
}
}
}
}while (spfa()){
if ((sum+dis[T]*flow[T])<0){
ans+=(int)(sum/(-dis[T])); break;
}else {
for (i=T; i!=S; i=e[pre[i]].u){
e[pre[i]].cap-=flow[T];
e[pre[i]^1].cap+=flow[T];
}ans+=flow[T],sum+=dis[T]*flow[T];
}
}printf("%d\n",ans);
return 0;
}


T3

题目大意:

给出一棵n个节点的树,每次操作选一条链,用一个自变量是和出发点的距离的一次函数更新最小值,多次询问某条链的最小值。

题解:

据说这个题是李超树,国家集训队作业题挂到了树上(听说qdez的神犇们都做过。。。)

用线段树维护覆盖当前区间的线段,更新的时候分情况讨论,如果可以更新就下传当前线段,然后标记新线段,每次线段最多被分成log(n)个区间,下放最多log(n)层,算上树链剖分,总的复杂度是O(nlog3(n))。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define inf 123456789123456789ll
#define LL long long
#define root 1,1,n
#define lch rt<<1,l,mid
#define rch rt<<1|1,mid+1,r
struct E{
int v,next; LL k;
}e[N<<1];
struct Line{
LL k,b;
inline LL F(LL x){
return k*x+b;
}
};
struct Tr{
Line x; LL minn; bool f;
}tr[N<<2];
int n,m,num=0,tot=0,head
,cur
,fa
[20],de
,w
,top
,sz
,son
,q
; LL ans,dis
,Dis
;
inline int in(){
int x=0; char ch=getchar(); bool f=1;
while (ch<'0'||ch>'9'){
if (ch=='-') f=0;
ch=getchar();
}while (ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
if (!f) x=-x; return x;
}
inline LL Lin(){
LL x=0ll; char ch=getchar(); bool f=1;
while (ch<'0'||ch>'9'){
if (ch=='-') f=0;
ch=getchar();
}while (ch>='0'&&ch<='9')
x=x*10ll+(LL)(ch-'0'),ch=getchar();
if (!f) x=-x; return x;
}
inline void add(int u,int v,LL k){
e[++num].v=v,e[num].k=k;
e[num].next=head[u],head[u]=num;
}
inline void BFS(){
int i,j,u,v,h=0,t=1; q[h]=1,dis[1]=0;
for (i=1; i<=n; i++) cur[i]=head[i];
while (h<t){
u=q[h++];
for (i=1; (1<<i)<=de[u]; i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for (i=head[u]; i; i=e[i].next){
v=e[i].v; if (v==fa[u][0]) continue;
fa[v][0]=u,de[v]=de[u]+1;
dis[v]=dis[u]+e[i].k,q[t++]=v;
}
}for (i=t-1; i>=0; i--){
u=q[i],sz[u]=1,son[u]=0;
for (j=head[u]; j; j=e[j].next){
v=e[j].v; if (v==fa[u][0]) continue;
sz[u]+=sz[v]; if (sz[son[u]]<sz[v]) son[u]=v;
}
}for (i=0; i<t; i++){
u=q[i];
if (!w[u]){
for (j=u; j; j=son[j])
w[j]=++tot,Dis[tot]=dis[j],top[j]=u;
}
}
}
inline int lca(int x,int y){
if (de[x]<de[y]) swap(x,y);
int i,k=de[x]-de[y];
for (i=18; i>=0; i--)
if (k&(1<<i)) x=fa[x][i];
if (x==y) return x;
for (i=18; i>=0; i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
if (x==y) return x;
return fa[x][0];
}
inline void push_up(int rt){
tr[rt].minn=min(tr[rt].minn,min(tr[rt<<1].minn,tr[rt<<1|1].minn));
}
inline void build(int rt,int l,int r){
tr[rt].minn=inf,tr[rt].f=0;
if (l==r) return;
int mid=(l+r)>>1;
build(lch),build(rch);
push_up(rt);
}
inline void change(int rt,int l,int r,int ll,int rr,Line x){
if (ll<=l&&r<=rr){
tr[rt].minn=min(tr[rt].minn,min(x.F(Dis[l]),x.F(Dis[r])));
if (!tr[rt].f){
tr[rt].f=1,tr[rt].x=x;
return;
}if (l==r){
if (x.F(Dis[l])<tr[rt].x.F(Dis[l])) tr[rt].x=x;
return;
}int mid=(l+r)>>1;
LL k1=x.F(Dis[l]),k2=tr[rt].x.F(Dis[l]);
LL kk1=x.F(Dis[mid]),kk2=tr[rt].x.F(Dis[mid]);
LL kkk1=x.F(Dis[r]),kkk2=tr[rt].x.F(Dis[r]);
if (k1>k2&&kkk1>kkk2) return;
if (k1<k2&&kkk1<kkk2){
tr[rt].x=x; return;
}if (kk1<kk2)
swap(x,tr[rt].x),swap(k1,k2),swap(kkk1,kkk2);
if (k1<k2) change(lch,ll,rr,x);
if (kkk1<kkk2) change(rch,ll,rr,x);
return;
}int mid=(l+r)>>1;
if (ll<=mid) change(lch,ll,rr,x);
if (rr>mid) change(rch,ll,rr,x);
push_up(rt);
}
inline LL query(int rt,int l,int r,int ll,int rr){
if (ll<=l&&r<=rr) return tr[rt].minn;
int mid=(l+r)>>1; LL k=inf;
if (tr[rt].f) k=min(k,min(tr[rt].x.F(Dis[max(ll,l)]),tr[rt].x.F(Dis[min(rr,r)])));
if (ll<=mid) k=min(k,query(lch,ll,rr));
if (rr>mid) k=min(k,query(rch,ll,rr));
return k;
}
inline void work_change(int x,int y,LL z,LL zz){
int f=lca(x,y); LL k1,b1,k2,b2;
k1=-z,b1=z*dis[x]+zz;
k2=z,b2=z*(-2*dis[f]+dis[x])+zz;
while (de[top[x]]>de[f]){
change(root,w[top[x]],w[x],(Line){k1,b1});
x=fa[top[x]][0];
}while (de[top[y]]>de[f]){
change(root,w[top[y]],w[y],(Line){k2,b2});
y=fa[top[y]][0];
}if (x!=f) change(root,w[f],w[x],(Line){k1,b1});
else if (y!=f) change(root,w[f],w[y],(Line){k2,b2});
else change(root,w[f],w[f],(Line){k1,b1});
}
inline void work_query(int x,int y){
ans=inf;
while (top[x]!=top[y]){
if (de[top[x]]<de[top[y]]) swap(x,y);
ans=min(ans,query(root,w[top[x]],w[x]));
x=fa[top[x]][0];
}if (de[x]<de[y]) swap(x,y);
ans=min(ans,query(root,w[y],w[x]));
printf("%lld\n",ans);
}
int main(){
n=in(),m=in(); int i,op,x,y; LL z,zz;
for (i=1; i<n; i++){
x=in(),y=in(),z=Lin();
add(x,y,z),add(y,x,z);
}BFS(),build(root);
for (i=1; i<=m; i++){
op=in();
if (op==1){
x=in(),y=in(),z=Lin(),zz=Lin();
work_change(x,y,z,zz);
}else {
x=in(),y=in();
work_query(x,y);
}
}return 0;
}


Day2

T1

题目大意:

开始有一个空串,n次操作,每次在串的最后新添一个字符,每次操作完后求此时串中不同字串的个数。

题解:

SA+线段树。对原串建前缀数组,然后问题转变成了每次加入i个前缀,求有多少之前没出现过的。那就可以用线段树维护一下当前rank的前驱和后继,查一下他们之间的lcp,加加减减就好了(zkw好写好调啊)。

Ps. 据说是SAM裸题,然而我并不会啊。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 200010
#define LL long long
int n,m,tot,a
,sa
,rank
,cc
,h
,tt
,height[N<<2],tr[N<<2]; LL ans=0;
inline int in(){
int x=0; char ch=getchar(); bool f=1;
while (ch<'0'||ch>'9'){
if (ch=='-') f=0;
ch=getchar();
}while (ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
if (!f) x=-x; return x;
}
inline bool cmp(int x,int y){
return a[x]<a[y];
}
inline void SA(){
int i,j;
for (i=1; i<=n; i++) sa[i]=i;
sort(sa+1,sa+n+1,cmp);
for (i=1,m=0; i<=n; i++){
m++,rank[sa[i]]=m;
while (i<n&&a[sa[i]]==a[sa[i+1]])
i++,rank[sa[i]]=m;
}for (j=1; j<=n&&m<n; j<<=1){
for (i=0; i<=m; i++) cc[i]=0;
for (i=1; i<=n; i++) cc[rank[i+j]]++;
for (i=1; i<=m; i++) cc[i]+=cc[i-1];
for (i=1; i<=n; i++) tt[cc[rank[i+j]]--]=i;
for (i=0; i<=m; i++) cc[i]=0;
for (i=1; i<=n; i++) cc[rank[i]]++;
for (i=1; i<=m; i++) cc[i]+=cc[i-1];
for (i=n; i>=1; i--) sa[cc[rank[tt[i]]]--]=tt[i];
for (i=1,m=0; i<=n; i++){
m++,tt[sa[i]]=m;
while (i<n&&rank[sa[i]]==rank[sa[i+1]]&&rank[sa[i]+j]==rank[sa[i+1]+j])
i++,tt[sa[i]]=m;
}for (i=1; i<=n; i++) rank[i]=tt[i];
}for (tot=1; tot<n+5; tot<<=1);
for (i=1; i<=n; i++){
if (rank[i]==1) continue;
h[i]=max(0,h[i-1]-1),j=sa[rank[i]-1];
while (a[i+h[i]]==a[j+h[i]]) h[i]++;
}for (i=1; i<=n; i++) height[rank[i]+tot]=h[i];
for (i=tot-1; i>=1; i--) height[i]=min(height[i<<1],height[i<<1|1]);
}
inline void add(int x){
for (x=x+tot; x; x>>=1) tr[x]=1;
}
inline int Last(int x){
for (x=x+tot; x; x>>=1)
if ((x&1)&&tr[x^1]) break;
for (x=x^1; x<tot;){
x<<=1;
if (tr[x|1]) x|=1;
}return x-tot;
}
inline int Next(int x){
for (x=x+tot; x; x>>=1)
if ((!(x&1))&&tr[x^1]) break;
for (x=x^1; x<tot;){
x<<=1;
if (!tr[x]) x|=1;
}return x-tot;
}
inline int query(int l,int r){
int s=n; l+=tot,r+=tot+1;
for (; l^r^1; l>>=1,r>>=1){
if (!(l&1)) s=min(s,height[l^1]);
if (r&1) s=min(s,height[r^1]);
}return s;
}
int main(){
n=in(); int i,x,l,r;
for (i=n; i>=1; i--) a[i]=in();
SA(),add(0),add(n+1);
for (i=n; i>=1; i--){
ans+=(LL)(n-i+1),x=rank[i];
l=Last(x),r=Next(x),add(x);
if (1<=l&&r<=n) ans+=(LL)query(l,r);
if (1<=l) ans-=(LL)query(l,x);
if (r<=n) ans-=(LL)query(x,r);
printf("%I64d\n",ans);
}return 0;
}


T2

题目大意:

求长度为n的排列中恰好有k个第i位是i的方案数。

题解:

考虑f[m]表示一个长度为m排列中m个数全都不在原位的方案数,那一个长度为n(n>m)的排列中恰好有n−m个数在原位的方案是Cmn×f[m],显然f[m]=(m−1)(f[m−1]+f[m−2]),因为有取模,所以可以预处理出f[n]、1−n的阶乘和1!−n!的逆元,O(n)预处理,O(1)询问。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000000
#define P 1000000007ll
#define LL long long
int n,m; LL fac[N+10],inv[N+10],f[N+10];
inline int in(){
int x=0; char ch=getchar(); bool f=1;
while (ch<'0'||ch>'9'){
if (ch=='-') f=0;
ch=getchar();
}while (ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
if (!f) x=-x; return x;
}
inline LL C(int x,int y){
return (((fac[x]*inv[y])%P)*inv[x-y])%P;
}
inline LL qp(LL x,LL y){
LL z=1;
while (y){
if (y&1) z=(z*x)%P;
x=(x*x)%P,y>>=1;
}return z;
}
int main(){
int T=in(),tt,i;
fac[0]=1ll,f[0]=1,f[1]=0,f[2]=1;
for (i=1; i<=N; i++)
fac[i]=fac[i-1]*(LL)i%P;
inv
=qp(fac
,P-2);
for (i=N-1; i>=0; i--)
inv[i]=inv[i+1]*(LL)(i+1)%P;
for (i=3; i<=N; i++)
f[i]=(LL)(i-1)*(f[i-1]+f[i-2])%P;
for (tt=1; tt<=T; tt++){
n=in(),m=in();
printf("%I64d\n",C(n,m)*f[n-m]%P);
}return 0;
}


T3

题目大意:

给出n个数,划分成m块,每块的权值为块内所有数的和,设最小的方差为Ans,求Ans∗m2。

题解:

DP+斜率优化。化式子:

令xx为平均数,s[i]为前i个数的和。

Ans=1m∑mi=1(xi−xx)2

=1m∑mi=1(x2i−2×xi×xx+xx2)

=1m∑mi=1(x2i)−1m∑mi=1(2×xi×xx)+xx2

=1m∑mi=1(x2i)−1m×2×s[m]×xx+xx2

=1m∑mi=1(x2i)−2×s[m]2m2+s[m]2m2

=1m∑mi=1(x2i)−s[m]2m2

Ans×m2=m∑mi=1x2i−s[m]2

就是求最小平方和,直接搞斜率优化就好了。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 3010
#define LL long long
struct Q{
LL x,y;
}q[N<<1];
int n,m,a
,s
; LL f

,ans;
inline int in(){
int x=0; char ch=getchar(); bool f=1;
while (ch<'0'||ch>'9'){
if (ch=='-') f=0;
ch=getchar();
}while (ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
if (!f) x=-x; return x;
}
inline void dp(int xu){
int i,h=1,t=1; LL x,y,x1,y1,x2,y2;
q[h].x=(LL)s[xu-1]*2;
q[h].y=f[xu-1][xu-1]+(LL)s[xu-1]*s[xu-1];
for (i=xu; i<=n; i++){
while (h<t){
x=q[h].y-q[h].x*(LL)s[i];
y=q[h+1].y-q[h+1].x*(LL)s[i];
if (x>=y) h++;
else break;
}f[xu][i]=q[h].y-q[h].x*(LL)s[i]+(LL)s[i]*(LL)s[i];
x=(LL)s[i]*2,y=f[xu-1][i]+(LL)s[i]*(LL)s[i];
while (h<t){
x1=x-q[t-1].x,y1=y-q[t-1].y;
x2=q[t].x-q[t-1].x,y2=q[t].y-q[t-1].y;
if (x2*y1-x1*y2<=0) t--;
else break;
}t++,q[t].x=x,q[t].y=y;
}
}
int main(){
n=in(),m=in(); int i;
for (i=1; i<=n; i++)
a[i]=in(),s[i]=s[i-1]+a[i];
for (i=1; i<=n; i++)
f[1][i]=(LL)s[i]*(LL)s[i];
for (i=2; i<=m; i++) dp(i);
ans=f[m]
*(LL)m-(LL)s
*(LL)s
;
printf("%I64d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: