20190811 NOIP模拟测试17 「入阵曲 · 将军令 · 星空 」
T1 入阵曲
将二维压成一维来做,维护每一列的前缀和,n^2枚举最上一行和最下一行,把中间这几行当成一行来做,维护一个桶记录这几行前几列的和的余数是x的个数,因为在模K意义下余数相同的两个数的差是K的倍数 时间复杂度 O(n^3)
#include<iostream> #include<cstdio> using namespace std; int n,m,K,a[410][410],sum[410][410],tmp[1100000],p[1100000]; inline int read(){ register int ret; register char r; while(r=getchar(),r<'0'||r>'9'); ret=r^48; while(r=getchar(),r>='0'&&r<='9') ret=(ret<<1)+(ret<<3)+(r^48); return ret; } int main(){ n=read(),m=read(),K=read(); for(register int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(), sum[i][j]=(sum[i-1][j]+a[i][j])%K;//维护第j列前缀和 long long ans=0; for(register int i=1;i<=n;i++){ for(register int j=i;j<=n;j++){ int cet=0; for(register int l=1;l<=m;l++){ cet=((cet+sum[j][l]-sum[i-1][l])%K+K)%K; ans+=tmp[cet]++; if(cet==0) ans++; if(tmp[cet]==1) p[++p[0]]=cet; } for(int l=1;l<=p[0];l++) tmp[p[l]]=0; p[0]=0; } } printf("%lld\n",ans); }
T2 将军令
可以想到像 小胖守皇宫 那样的树上DP ,k==1的情况可以做,k==2的情况也可以YY掉,但是K==3或20的情况就不好YY了(但是听说洛谷上有写DP 通式的,码量很小)
正解是贪心,无根树变有根树,DFS 一遍,用大根堆维护深度最深的点,从该点向上找距他为K的点(如果距离小于K就走到了根,那就用根就行了),然后从这个点在整棵树上找距他小于等于K的点,以后从堆里拿出标记的点直接跳即可。 注意在标记的时候,千万不要碰到一个标记过的点就return,有可能这个点的儿子没有被覆盖,而当前驻扎小队的点会覆盖它的儿子
PS :这样会TLE 95 ,可以记录如果一个点的所有子孙都被覆盖了(标记过),在开一个数组,把该点标记,这种点是可以跳的
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> using namespace std; int n,d,t,tot; int to[210000],head[110000],nex[210000],fa[210000],v[210000],al[210000]; struct node{ int dep,id; bool operator < (node b)const{ return dep<b.dep; } node(int a,int b){id=a,dep=b;} }; priority_queue<node>q; void add(int x,int y){ to[++tot]=y,nex[tot]=head[x],head[x]=tot; } void dfs(int x,int pre,int dis){ q.push(node(x,dis+1)); fa[x]=pre; for(int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==pre) continue; dfs(y,x,dis+1); } } void work(int x,int k){ v[x]=1; if(nex[head[x]]==0){ if(to[head[x]]==fa[x]||al[to[head[x]]]) al[x]=1; } if(k==0) return; for(int i=head[x];i;i=nex[i]){ int y=to[i]; if(al[y]) continue;//避免重复,陷入死循环 work(y,k-1);//走儿子的同时,也能走到爸爸 } for(int i=head[x];i;i=nex[i]){ int y=to[i]; if(y==fa[x]) continue; al[x]=min(al[y],al[x]); } } int main(){ scanf("%d%d%d",&n,&d,&t); for(int i=1,x,y;i<n;i++){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(1,0,0); int ans=0; while(q.size()){ int w=q.top().id; q.pop(); if(v[w]) continue; for(int i=1;i<=d&&w!=1;i++,w=fa[w]); ans++; work(w,d); } printf("%d\n",ans); }
T3 星空
https://www.luogu.org/problemnew/solution/P3943
#include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std; int n,K,m,num; int a[41000],b[70],c[20],v[41000],dis[20][41000],inq[41000],f[1<<17],to[1<<17]; void Dijistra(){ memset(dis,0x3f,sizeof(dis)); queue<int>q; for(int w=1;w<=num;w++){ q.push(c[w]); inq[c[w]]=1; dis[w][c[w]]=0; while(q.size()){ int x=q.front(); inq[x]=0; q.pop(); for(int i=1;i<=m;i++){ int y=x-b[i]; if(y>0&&dis[w][y]>dis[w][x]+1){ dis[w][y]=dis[w][x]+1; if(!inq[y]){ q.push(y); inq[y]=1; } } y=x+b[i]; if(y<=n&&dis[w][y]>dis[w][x]+1){ dis[w][y]=dis[w][x]+1; if(!inq[y]){ q.push(y); inq[y]=1; } } } } } } int main(){ scanf("%d%d%d",&n,&K,&m); n++; for(int i=1,x;i<=K;i++){ scanf("%d",&x); a[x]^=1,a[x+1]^=1; } for(int i=1;i<=m;i++){ scanf("%d",&b[i]); } for(int i=1;i<=n;i++){ if(a[i]) c[++num]=i; } Dijistra(); /* cout<<num<<endl; for(int i=1;i<=num;i++){ for(int j=1;j<=num;j++){ printf("%d %d %d\n",i,j,dis[i][c[j]]); } }*/ memset(f,0x3f,sizeof(f)); f[0]=0; for(int i=1;i<=num;i++) to[1<<(i-1)]=i; for(int i=0;i<=(1<<num)-1;i++){ for(int j=i;j;j-=j&(-j)){ int w=to[j&(-j)],sta=j-(j&(-j)); for(int l=sta;l;l-=l&(-l)){ int ww=to[l&(-l)]; f[i]=min(f[i],f[i^(j&(-j))^(l&(-l))]+dis[w][c[ww]]); } } } printf("%d\n",f[(1<<num)-1]); }
$Will$ $Be$ $The$ $King$