noip模拟10
A. 入阵曲
很经典的一道题目,维护具有特殊性质的前缀和,这个方法很常用..其实也是对前缀和特点的一种应用..
本题中维护模 k 相同的前缀和即可..
#include<bits/stdc++.h> using namespace std; #define ll long long int #define lf double #define mp make_pair inline void read(ll &ss) { ss=0; bool cit=0; char ch; while(!isdigit(ch=getchar())) if(ch=='-') cit=1; while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar(); if(cit) ss=-ss; } ll n,m,mod,ans; ll a[500][500]; ll pre[500][500]; ll f[(int)1e7]; ll g[(int)1e7]; signed main() { read(n); read(m); read(mod); for(ll i=1;i<=n;i++) { for(ll j=1;j<=m;j++) { read(a[i][j]); pre[i][j]=(pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+a[i][j]+mod*10)%mod; } } for(ll i=0;i<=n;i++) { for(ll j=i+1,cnt=0;j<=n;j++,cnt=0) { f[0]=1; for(ll k=1,temp;k<=m;k++) { temp=(pre[j][k]-pre[i][k]+mod*10)%mod; g[++cnt]=temp; ans+=f[temp]; f[temp]++; } for(ll k=1;k<=cnt;k++) f[g[k]]=0; } } printf("%lld",ans); return 0; } /* 2 3 2 1 2 1 2 1 2 */
B. 将军令
可以考虑树形dp:我们可以很明显地观察到,如果 k 全部都等于 1,那么很像之前做过的一道题《小胖守皇宫》,本题维护一下被 j 级子孙,j 级祖先对自己的影响即可..
另外考虑贪心..我们发现一个性质:无论何时,我们选择 k 级祖先来维护自己是最优的答案。用反证法证明,如果我们选择了比 k 级祖先更低的点来维护自己,那么一定会有更多维护能力被浪费掉,因为 k 级祖先维护的范围才是最广的..
而我考场上只打了一个爆搜,其实加个记忆化就能 A,但是根本想不到,所以这里划重点:打爆搜一定要想想记忆化剪枝
#include<bits/stdc++.h> using namespace std; #define ll int #define lf double #define mp make_pair const ll N=1e5+50; inline void read(ll &ss) { ss=0; bool cit=0; char ch; while(!isdigit(ch=getchar())) if(ch=='-') cit=1; while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar(); if(cit) ss=-ss; } ll n,maxn,sp,ans,ts; ll head[N]; bool vis[N]; struct I { ll u,v,nxt; } a[N*20]; struct II { ll dep,num; } g[N+50]; bool operator <(const II &aa,const II &bb) { if(aa.dep==bb.dep) return aa.num<bb.num; return aa.dep<bb.dep; } priority_queue<II> que; inline void add(ll u,ll v) { a[++ts].u=u; a[ts].v=v; a[ts].nxt=head[u]; head[u]=ts; } ll find(ll now,ll left) { if(left==0 or now==0) return now; for(ll i=head[now];i;i=a[i].nxt) { if(i and g[a[i].v].dep<g[now].dep) return find(a[i].v,left-1); } } void dfs_depth(ll now,ll depth) { vis[now]=true; g[now].num=now; g[now].dep=depth; que.push(g[now]); for(ll i=head[now];i;i=a[i].nxt) { if(vis[a[i].v]) continue; dfs_depth(a[i].v,depth+1); } return ; } void dfs(ll dad,ll now,ll left) { vis[now]=true; if(left==0 or now==0) return ; for(ll i=head[now];i;i=a[i].nxt) { if(a[i].v==dad) continue; dfs(now,a[i].v,left-1); } return ; } signed main() { read(n); read(maxn); read(sp); ll u,v; for(ll i=1;i<=n-1;i++) { read(u); read(v); add(u,v); add(v,u); } dfs_depth(1,1); memset(vis,0,sizeof vis); II temp; ll tem; while(!que.empty()) { temp=que.top(); que.pop(); if(vis[temp.num]) continue; ans++; tem=find(temp.num,maxn); dfs(tem,tem,maxn); } printf("%d",ans); return 0; }
C. 星空
又是一道观察性质的题目..(大概是因为我太菜,一到这类题就只能认为是观察性质,怎么想都想不到..)
观察 k 的范围,理应想到状压..
然后考虑题目的特点:发现可以类比’开心消消乐‘..(题解里面还管这个叫广义前缀和,不过想一想还真的是有用到前缀和的特点..)
#include<bits/stdc++.h> using namespace std; #define ll int #define lf double #define mp make_pair const ll N=1e5+50; inline void read(ll &ss) { ss=0; bool cit=0; char ch; while(!isdigit(ch=getchar())) if(ch=='-') cit=1; while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar(); if(cit) ss=-ss; } ll n,m,t,ts; bool p[N],g[N],vis[N]; ll op[N],pos[N],org[N]; ll head[N],f[1<<20]; ll dis[200][N]; deque<ll> que; inline void bfs(ll now) { dis[now][pos[now]]=0; que.push_back(pos[now]); vis[pos[now]]=1; ll x,y; while(que.size()) { x=que.front(); que.pop_front(); for(ll i=1;i<=t;i++) { y=x-op[i]; if(y>0 and dis[now][y]>dis[now][x]+1) { dis[now][y]=dis[now][x]+1; que.push_back(y); vis[y]=1; } y=x+op[i]; if(y<=n+1 and dis[now][y]>dis[now][x]+1) { dis[now][y]=dis[now][x]+1; que.push_back(y); vis[y]=1; } } vis[x]=0; } return ; } signed main() { read(n); read(m); read(t); ll temp; memset(p,1,sizeof p); for(ll i=1;i<=m;i++) { read(temp); p[temp]=false; } for(ll i=1;i<=t;i++) read(op[i]); sort(op+1,op+1+t); temp=0; p[0]=true; for(ll i=1;i<=n+1;i++) { g[i]=p[i] xor p[i-1]; if(g[i]) pos[++temp]=i,org[i]=temp; } memset(dis,0x3f,sizeof dis); memset(f,0x3f,sizeof f); for(ll i=1;i<=temp;i++) bfs(i); f[(1<<temp)-1]=0; for(ll i=(1<<temp)-1,j,v;i>=0;i--) { j=1; while(((1<<(j-1)) & i)==0 and j<temp) j++; for(ll k=j+1;k<=temp;k++) { if((i & (1<<(k-1)))==0) continue; v=(i xor (1<<(j-1))) xor (1<<(k-1)); f[v]=min(f[v],f[i]+dis[j][pos[k]]); } } printf("%d",f[0]); return 0; } /* 5 2 2 1 5 3 4 */