T1看了看不可做跳过了,T2看了看不好做跳过了,于是自然地觉得又是难度倒序排列倒着做。最后过了后两题,200/300。
T1.设计图案
题目大意:给定一个n*m的01矩阵,用1*2的方块或k个环覆盖所有的1,每个方案的权值是2^k,求所有方案的权值和。(n*m<=300)
思路:看上去是插头DP,实际上是一道结论题,下面给出题解的结论:
AC代码
#include<cstdio> #include<cstring> #define MN 300 int a[MN+5][MN+5],f[2][1<<17]; int main() { freopen("design.in","r",stdin); freopen("design.out","w",stdout); int n,m,p,i,j,k,x,p1,p2; scanf("%d%d%d",&n,&m,&p); for(i=1;i<=n;++i)for(j=1;j<=m;++j)scanf("%d",&(m>n?a[j][i]:a[i][j])); if(m>n)i=n,n=m,m=i;f[0][0]=1; for(i=p1=1,p2=0;i<=n+1;++i)for(j=1;j<=m;++j,p1^=1,p2^=1) { memset(f[p1],0,sizeof(f[p1])); for(k=0;k<1<<m;++k) { if(!a[i-1][j]&&(k&(1<<j-1)))continue; x=k^(a[i-1][j]<<j-1); if(!a[i][j]&&(x&(1<<j-1)))continue; f[p1][x]=(f[p1][x]+f[p2][k])%p; if(a[i][j]&&a[i][j-1]&&!(x&(1<<j-1))&&!(x&(1<<j-2))) x|=1<<j-1,x|=1<<j-2,f[p1][x]=(f[p1][x]+f[p2][k])%p; } } printf("%d",1ll*f[p2][0]*f[p2][0]%p); fclose(stdin);fclose(stdout);return 0; }
T2.最优发射
题目大意:给定长度为L的环上n条线段,m个询问,每次询问ki个点的最小线段覆盖,无解输出-1。(n,m<=100,000,ki之和<=200,000,L<=10^9)
思路:被包含的线段肯定不优,先删去,然后按左端点排序。每组询问可以对每个点二分出包含这个点的区间(不存在即无解)向右最多覆盖到哪个点,向最远那个点的再右边一个连边,得到一个基环外向树,目的即求走长度为L的路径至少需要几条边,正解说直接倍增,我深受数据结构毒害,大致做法是枚举一条边把环拆开,用Link-Cut Tree维护剩下的树形结构,复杂度O(nlogn)。
LCT:
#include<cstdio> #include<algorithm> using namespace std; inline int read() { int x=0;char c; while((c=getchar())<'0'||c>'9'); for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0'; return x; } #define MN 200000 int n,L,a[MN+5],t[MN+5],q[MN+5]; struct P{int l,r;}p[MN+5]; bool cmp(P a,P b){return a.l==b.l?a.r>b.r:a.l<b.l;} int c[MN+5][2],fa[MN+5],s[MN+5],tf[MN+5]; inline void update(int x){s[x]=s[c[x][0]]+s[c[x][1]]+1;} void rotate(int x) { int f=fa[x],ff=fa[f],l,r; l=c[f][1]==x;r=l^1; fa[f]=x;fa[x]=ff;fa[c[x][r]]=f; c[ff][c[ff][1]==f]=x;c[f][l]=c[x][r];c[x][r]=f; update(f);if(tf[f])tf[x]=tf[f],tf[f]=0; } void splay(int x) { for(int f;fa[x];rotate(x)) if(fa[f=fa[x]])rotate(c[fa[f]][0]==f^c[f][0]==x?x:f); update(x); } void access(int x) { int f; for(splay(x);(f=tf[x])>0;splay(x)) { splay(f); fa[c[f][1]]=0;tf[c[f][1]]=f; fa[c[f][1]=x]=f;tf[x]=0; } } int get(int x,int k) { int l=1,r=n,mid,res=0; while(l<=r) { if(p[mid=l+r>>1].l>a[x])r=mid-1; else l=mid+1,res=mid; } if(!res||p[res].r<a[x])return -1; if(p[res].r-L>=a[1]) { for(l=1,r=x;l<=r;) if(a[mid=l+r>>1]>p[res].r-L)q[x]=mid,r=mid-1; else l=mid+1; return t[x]=k+1,0; } else q[x]=0; for(l=x,r=k;l<=r;) if(a[mid=l+r>>1]>p[res].r)r=mid-1; else l=t[x]=mid+1; return 0; } int main() { freopen("launch.in","r",stdin); freopen("launch.out","w",stdout); int m,i,j,k,ans; n=read();m=read();L=read(); for(i=1;i<=n;++i) { p[i].l=read();p[i].r=read(); if(p[i].r<p[i].l)p[i+1].l=0,p[i+1].r=p[i].r,p[i].r+=L,++i,++n; } sort(p+1,p+n+1,cmp); for(i=1,p[j=0].r=-1;i<=n;++i)if(p[i].r>p[j].r)p[++j]=p[i];n=j; while(m--) { for(k=read(),i=1;i<=k;++i)a[i]=read(); sort(a+1,a+k+1); for(i=1,a[j=0]=-1;i<=k;++i)if(a[i]!=a[i-1])a[++j]=a[i]; for(i=1;i<=j;++i)if(get(i,j))break; if(i<=j){puts("-1");continue;} for(i=0;i++<=j;)c[i][0]=c[i][1]=fa[i]=0,s[i]=1,tf[i]=-1; for(ans=n,i=1;i<=j;++i) { if(q[i])ans=min(ans,(access(q[i]),s[c[q[i]][0]]+1)); splay(i);tf[i]=t[i]; } printf("%d ",min(ans,(access(1),s[c[1][0]]))); } fclose(stdin);fclose(stdout);return 0; }
倍增:
#include<cstdio> #include<algorithm> #define ll long long using namespace std; inline int read() { int x=0;char c; while((c=getchar())<'0'||c>'9'); for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0'; return x; } #define MN 200000 #define MK 20 int n,L,a[MN+5];ll t[MK][MN+5]; struct P{int l,r;}p[MN+5]; bool cmp(P a,P b){return a.l==b.l?a.r>b.r:a.l<b.l;} int get(int x,int k) { int l=1,r=n,mid,res=0; while(l<=r) { if(p[mid=l+r>>1].l>a[x])r=mid-1; else l=mid+1,res=mid; } if(!res||p[res].r<a[x])return -1; if(p[res].r>=L) for(l=1,r=x;l<=r;) if(a[mid=l+r>>1]>p[res].r-L)r=mid-1,t[0][x]=mid+k-x; else l=mid+1; else for(l=x,r=k;l<=r;) if(a[mid=l+r>>1]>p[res].r)r=mid-1; else l=mid+1,t[0][x]=mid+1-x; return 0; } int main() { freopen("launch.in","r",stdin); freopen("launch.out","w",stdout); int m,i,j,k,ans,s,f; n=read();m=read();L=read(); for(i=1;i<=n;++i) { p[i].l=read();p[i].r=read(); if(p[i].r<p[i].l)p[i+1].l=0,p[i+1].r=p[i].r,p[i].r+=L,++i,++n; } sort(p+1,p+n+1,cmp); for(i=1,p[j=0].r=-1;i<=n;++i)if(p[i].r>p[j].r)p[++j]=p[i];n=j; while(m--) { for(k=read(),i=1;i<=k;++i)a[i]=read(); sort(a+1,a+k+1); for(i=1,a[j=0]=-1;i<=k;++i)if(a[i]!=a[i-1])a[++j]=a[i]; for(i=1;i<=j;++i)if(get(i,j))break; if(i<=j){puts("-1");continue;} for(k=1;k<MK;++k)for(i=1;i<=j;++i) t[k][i]=t[k-1][i]+t[k-1][(i+t[k-1][i]-1)%j+1]; for(ans=n,i=1;i<=j;++i) { for(s=f=0,k=MK;k--;)if(s+t[k][(i+s-1)%j+1]<j)s+=t[k][(i+s-1)%j+1],f+=1<<k; ans=min(ans,f+1); } printf("%d ",ans); } fclose(stdin);fclose(stdout);return 0; }
T3.传输网络
题目大意:有N个发信器编号1~N,M个装置,每个装置(Ai,Bi,Ci,Di)满足1<=Ai<=Ci<=Bi<=N可以将[Ai,Bi]内的所有信息转移到Ci上,花费为Di,你决定M个装置是否启用,要求从任意一个发信器发出信息按顺序经过装置后都被发到同一个位置上,求最小花费,无解输出-1。(N<=10^9,M<=10^5)
思路:结论:若1号和N号被发射到同一个位置上,则其他发信器也会被发到这个位置上。这个结论画画图大概就懂了,不多做说明。考虑将1号N号分开DP,用f[i]记下1号发射到i处需要的最小花费,每次转移用线段树维护下,N号同理,每个装置可以把1号和N号发射到这个区间内的最小代价加在一起统计答案。复杂度O(mlogm)。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; inline int read() { int x=0;char c; while((c=getchar())<'0'||c>'9'); for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0'; return x; } #define MN 100000 #define N 131072 #define INF (1LL<<60) int c[MN+5],cn,l[MN+5],r[MN+5],t[MN+5],w[MN+5]; ll ans=INF,t1[N*2],t2[N*2]; void renew(ll*t,int k,ll x) { if(x>=t[k+=N])return; for(t[k]=x;k>>=1;)t[k]=min(t[k<<1],t[(k<<1)+1]); } ll query(ll*t,int l,int r) { ll res=INF; for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1) { if(~l&1)res=min(res,t[l+1]); if( r&1)res=min(res,t[r-1]); } return res; } int findl(int x) { int l=1,r=cn,mid,res; while(l<=r) if(c[mid=l+r>>1]<x)l=mid+1; else r=mid-1,res=mid; return res; } int findr(int x) { int l=1,r=cn,mid,res; while(l<=r) if(c[mid=l+r>>1]>x)r=mid-1; else l=mid+1,res=mid; return res; } int main() { freopen("network.in","r",stdin); freopen("network.out","w",stdout); int n,m,i,j,x,y,z;ll a,b; n=read();c[++cn]=1;c[++cn]=m=read(); for(i=1;i<=n;++i)l[i]=read(),r[i]=read(),c[++cn]=t[i]=read(),w[i]=read(); sort(c+1,c+cn+1); for(i=1,j=0;i<=cn;++i)if(c[i]!=c[i-1])c[++j]=c[i];cn=j; memset(t1,42,sizeof(t1));memset(t2,42,sizeof(t2)); renew(t1,1,0);renew(t2,cn,0); for(i=1;i<=n;++i) { x=findl(l[i]);y=findr(r[i]);z=findl(t[i]); a=query(t1,x,y);b=query(t2,x,y); if(a+b+w[i]<ans)ans=a+b+w[i]; renew(t1,z,a+w[i]);renew(t2,z,b+w[i]); } printf("%lld",ans<INF?ans:-1); fclose(stdin);fclose(stdout);return 0; }