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 }
T1

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 }
T2

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 }
T3

 

posted @ 2021-06-29 17:19  keen_z  阅读(25)  评论(0编辑  收藏  举报