2021.6.29考试总结[NOIP模拟10]
T1 入阵曲
二位前缀和暴力n4可以拿60。
观察到维护前缀和时模k意义下余数一样的前缀和相减后一定被k整除,前缀和维护模数,n2枚举行数,n枚举列,
开一个桶记录模数出现个数,每枚举到该模数就加上它先前出现个数,表示增添了这么多对可被k整除的前缀和。
code:
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 const int NN=405; 5 int n,m,k,a[NN][NN],sum[NN][NN],ans,bot[1000010],res[NN]; 6 bool check; 7 inline int read(){ 8 int x=0; 9 char ch=getchar(); 10 while(ch<'0'||ch>'9') ch=getchar(); 11 while(ch>='0'&&ch<='9'){ 12 x=(x<<1)+(x<<3)+(ch^48); 13 ch=getchar(); 14 } 15 return x; 16 } 17 signed main(){ 18 n=read(); m=read(); k=read(); 19 for(register int i=1;i<=n;++i) 20 for(register int j=1;j<=m;++j) a[i][j]=read(); 21 for(register int i=1;i<=n;++i) 22 for(register int j=1;j<=m;++j) sum[i][j]=(sum[i][j-1]+a[i][j])%k; 23 for(register int i=2;i<=n;++i) 24 for(register int j=1;j<=m;++j) sum[i][j]=(sum[i-1][j]+sum[i][j])%k; 25 for(register int i=1;i<=n;i++) 26 for(register int j=0;j<i;j++){ 27 for(register int u=1;u<=m;u++){ 28 res[u]=(sum[i][u]-sum[j][u]+k)%k; 29 ans+=bot[res[u]]++; 30 } 31 ans+=bot[0]; bot[0]=0; 32 for(register int u=1;u<=m;u++) 33 bot[res[u]]=0; 34 } 35 printf("%d\n",ans); 36 return 0; 37 }
T2 将军令
一眼看上去像个树形DP,但k<=20忒恶心了,跑DP麻烦至极,我两小时打DP爆拿10分(当然也可以做,大概
正解是一个贪心。观察到在深度较深的点驻扎军队更优。所以按深度逆序遍历。
为避免军队范围被浪费,每次发现点未被覆盖时在它的k级父亲处驻扎军队肯定最优。
驻扎时直接暴力DFS即可。
code:
1 #include<bits/stdc++.h> 2 #define re register 3 using namespace std; 4 const int NN=1e5+5; 5 int n,k,t,to[2*NN],nex[2*NN],head[NN],num,fa[NN][21],ans; 6 bool vis[NN]; 7 struct node{ 8 int id,dep; 9 }nod[NN]; 10 inline int read(){ 11 int x=0,f=1; 12 char ch=getchar(); 13 while(ch<'0'||ch>'9') ch=getchar(); 14 while(ch>='0'&&ch<='9'){ 15 x=(x<<1)+(x<<3)+(ch^48); 16 ch=getchar(); 17 } 18 return x*f; 19 } 20 inline void add(int a,int b){ 21 to[++num]=b; nex[num]=head[a]; head[a]=num; 22 to[++num]=a; nex[num]=head[b]; head[b]=num; 23 } 24 bool cmp(node a,node b){ 25 return a.dep>b.dep; 26 } 27 void indfs(int f,int s){ 28 nod[s].id=s; 29 for(int i=head[s];i;i=nex[i]){ 30 int t=to[i]; 31 if(t==f) continue; 32 nod[t].dep=nod[s].dep+1; 33 fa[t][1]=s; 34 for(int j=2;j<=min(k,nod[t].dep);j++) fa[t][j]=fa[fa[t][j-1]][1]; 35 indfs(s,t); 36 } 37 } 38 void dfs(int f,int s,int cnt){ 39 if(!cnt) return; 40 for(int i=head[s];i;i=nex[i]){ 41 int t=to[i]; 42 if(t==f) continue; 43 vis[t]=1; 44 dfs(s,t,cnt-1); 45 } 46 } 47 int main(){ 48 n=read(); k=read(); t=read(); 49 for(int i=1;i<n;i++) add(read(),read()); 50 indfs(0,1); 51 sort(nod+1,nod+n+1,cmp); 52 for(int i=1;i<=n;i++){ 53 if(vis[nod[i].id]) continue; 54 int st=fa[nod[i].id][min(nod[i].dep,k)]; 55 vis[st]=1; ++ans; 56 dfs(0,st,k); 57 } 58 printf("%d\n",ans); 59 return 0; 60 }
T3 星空
考场防AC的题。。至少对我来说。。
m=1的情况贪心扫一遍,碰到灭的就往后开,可以拿到28,再输出个2,36高分到手。
正解真不是人在考场上能想出来的(弱限制了我的想象
考虑对问题进行转化。
首先用差分数组将区间修改转化为单点修改。对[l,r]区间反转就相当于对l与r+1两点的差分值反转。
因为差分涉及r+1,所以要多维护一位。
之后问题就转化为每次异或两位,用最少操作数将01串全变为0。只考虑串里的1即可。
用BFS预处理出把两个1都变为0的最小方案,然后状压。
code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int NN=4e4+5; 4 int n,k,m,to[65],dis[20][20],tmp,f[1<<16]; 5 bool a[NN],pre[NN]; 6 map<int,int>mp; 7 inline int read(){ 8 int x=0,f=1; 9 char ch=getchar(); 10 while(ch<'0'||ch>'9') ch=getchar(); 11 while(ch>='0'&&ch<='9'){ 12 x=(x<<1)+(x<<3)+(ch^48); 13 ch=getchar(); 14 } 15 return x*f; 16 } 17 struct node{ 18 int id,w; 19 }c; 20 void bfs(int s){ 21 queue<node>q; 22 bool vis[NN]={0}; 23 c.id=s; c.w=0; 24 q.push(c); 25 while(!q.empty()){ 26 int x=q.front().id,y=q.front().w; 27 q.pop(); 28 if(vis[x]) continue; 29 vis[x]=1; 30 if(pre[x]&&dis[mp[s]][mp[x]]>999999999) dis[mp[s]][mp[x]]=y; 31 for(int i=1;i<=m;i++){ 32 int t1=x-to[i],t2=x+to[i]; 33 if(t1>=1&&!vis[t1]){ 34 c.id=t1; c.w=y+1; 35 q.push(c); 36 } 37 if(t2<=n+1&&!vis[t2]){ 38 c.id=t2; c.w=y+1; 39 q.push(c); 40 } 41 } 42 } 43 } 44 int main(){ 45 n=read(); k=read(); m=read(); 46 for(int i=1;i<=k;i++) a[read()]=1; 47 for(int i=1;i<=m;i++) to[i]=read(); 48 for(int i=1;i<=n+1;i++){ 49 pre[i]=a[i-1]^a[i]; 50 if(pre[i]) mp[i]=++tmp; 51 } 52 memset(dis,0x3f,sizeof(dis)); 53 memset(f,0x3f,sizeof(f)); 54 for(int i=1;i<=n+1;i++) 55 if(pre[i]) bfs(i); 56 int all=(1<<tmp)-1; 57 f[all]=0; 58 for(int u=all;u;u--) 59 for(int i=1;i<tmp;i++) 60 for(int j=i+1;j<=tmp;j++){ 61 if(i==j) continue; 62 if(!(u&(1<<(j-1)))&&!(u&(1<<(i-1)))) continue; 63 f[u^((1<<(i-1))|(1<<(j-1)))]=min(f[u^((1<<(i-1))|(1<<(j-1)))],f[u]+dis[i][j]); 64 } 65 printf("%d\n",f[0]); 66 return 0; 67 }