一道很好的题,综合很多知识点。

首先复习差分:

     将原来的每个点a[i]转化为b[i]=a[i]^a[i+1],(如果是求和形式就是b[i]=a[i+1]-a[i])

我们发现这样的方便在于我们可以运用前缀和的形式,求出单点值,当然,差分一般支持区间修改

单点查询,同时我们发现异或也满足转化的性质,我们发现异或的区间修改,也可以化为单点修改

然后进行问题转换:在一个序列中按要求修改端点,问最少修改多少次区间全部为0

把原来的每个数转化为差分形式,注意要多加一个b[0]=b[0]^b[1],然后我们发现假设修改

a[1]-a[5]的区间,那么在差分中实际影响的是b[0],b[5]两个值。

同时有一条显然的性质,差分数组中的1是偶数个。

然后再次转化问题:

我们已经知道每种操作的长度(实际在差分数组中长度+1,自己思考)

那么我们可以给数与数连边,连边长度为一,当两个1相遇后,相当于反转,变为0

那么我们将问题转化为图论问题:在n个节点图中,每个节点最多m个边,求出两两最短距离,

然后两个1相遇后全为0,求变为1的最小贡献

然后跑最短路

最后我们发现k的值很小,可以转为状压DP的形式

总的复杂度nmk+(k^2)*(2^k)

  1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<string>
5 #include<map>
6 #include<vector>
7 #include<set>
8 #include<algorithm>
9 #include<cmath>
10 #include<queue>
11 #define MAXN 3000001
12 using namespace std;
13 int a[MAXN],sum[MAXN];
14 int c[MAXN];
15 int head[MAXN],tot;
16 struct node{int to,n;}e[2700000*2];
17 void add(int u,int v)
18 {
19 e[++tot].to=v;e[tot].n=head[u];head[u]=tot;
20 }
21 int n,k,m;
22 int vis[MAXN];
23 int dis[301][301];
24 int spfa_dis[MAXN];
25 int the_1[MAXN];
26 int belong[MAXN];
27 void spfa(int root)
28 {
29 queue<int>q;
30 memset(vis,0,sizeof(vis));
31 memset(spfa_dis,0x3f3f3f,sizeof(spfa_dis));
32 q.push(root);
33 spfa_dis[root]=0;
34 while(!q.empty())
35 {
36 int x=q.front();q.pop();vis[x]=0;
37 for(int i=head[x];i;i=e[i].n)
38 {
39 int to=e[i].to;
40 if(vis[to])continue;
41 if(spfa_dis[to]>spfa_dis[x]+1)
42 {
43 spfa_dis[to]=spfa_dis[x]+1;
44 if(vis[to]==0)
45 {
46 q.push(to);
47 vis[to]=1;
48 }
49 }
50 }
51 }
52 for(int i=1;i<=the_1[0];++i)
53 {
54 int ci=the_1[i];
55 dis[belong[root]][belong[ci]]=spfa_dis[ci];
56 dis[belong[ci]][belong[root]]=spfa_dis[ci];
57 //printf("dis[%d][%d]=%d\n",belong[root],belong[ci],spfa_dis[ci]);
58 }
59 }
60 void init()
61 {
62 for(int i=0;i<=n;++i)//sum[i]=a[i]^a[i+1]
63 {
64 for(int j=1;j<=m;++j)
65 {
66 int me=i;
67 int l=i-c[j];
68 if(l>=0)
69 {
70 add(i,l);
71 //printf("add i=%d l=%d\n",i,l);
72 }
73 int r=i+c[j];
74 if(r<=n)
75 {
76 add(i,r);
77 //printf("add i=%d r=%d\n",i,r);
78 }
79 }
80 }
81 for(int i=0;i<=n;++i)
82 {
83 if(sum[i]==1)
84 spfa(i);
85 }
86 }
87 int fir=0,base[30];
88 int f[1<<18];
89 void DP()
90 {
91 for(int i=0;i<=n;++i)
92 {
93 fir|=sum[i]<<i;
94 }
95 base[0]=1;
96 for(int i=1;i<=n;++i)
97 {
98 base[i]=base[i-1]<<1;
99 }
100 memset(f,0x3f3f3f,sizeof(f));
101 f[base[the_1[0]]-1]=0;
102 for(int i=base[the_1[0]]-1;i>=0;--i)
103 {
104 int ci=i;
105 // printf("sr=%d\n",i);
106 for(int l=1;l<=the_1[0];++l)
107 {
108 if(((ci>>(l-1))&1)==1)
109 {
110 for(int r=l+1;r<=the_1[0];++r)
111 {
112 if(((ci>>(r-1))&1)==1)
113 {
114 f[i^base[l-1]^base[r-1]]=min(f[i^base[l-1]^base[r-1]],f[i]+dis[l][r]);
115 //printf("state=%d l=%d r=%d %d\n",f[i^base[l-1]^base[r-1]],l,r,dis[l][r]);
116 }
117 }
118 }
119 }
120 }
121 printf("%d\n",f[0]);
122 }
123 signed main()
124 {
125 scanf("%d%d%d",&n,&k,&m);
126 for(int i=1;i<=k;++i)
127 {
128 int x;
129 scanf("%d",&x);
130 a[x]=1;
131 }
132 for(int i=0;i<=n;++i)
133 {
134 sum[i]=a[i]^a[i+1];
135 if(sum[i]==1)
136 {
137 the_1[++the_1[0]]=i;
138 belong[i]=the_1[0];
139 }
140 //printf("sum[%d]=%d\n",i,sum[i]);
141 }
142 for(int i=1;i<=m;++i)
143 {
144 scanf("%d",&c[i]);
145 }
146 init();
147 DP();
148 }

最新文章

  1. C#分布式消息队列 EQueue 2.0 发布啦
  2. Linux ERRNO
  3. 深入理解javascript原型和闭包(7)——原型的灵活性
  4. 解决iOS9下隐藏App返回按钮文字导致的诡异闪屏问题
  5. git回滚到上一版本
  6. fiddler 挂载 JS文件
  7. codeforces343A A. Rational Resistance
  8. 高通、猎户机型Android典型bootloader分析
  9. 常用的 Android Studio 快捷键
  10. arm-elf-gcc汇编代码个人理解
  11. 「OC」 封装
  12. HYSBZ 2818 gcd
  13. haskell学习笔记&lt;1&gt;--基本语法
  14. 华为专家谈CMDB建设
  15. java集合介绍(List,Set,Map)
  16. win10 新建文件夹没有了
  17. 第五篇:数据备份、pymysql模块
  18. node path.resolve()
  19. Generative Model 与 Discriminative Model
  20. (实用)Linux下安装JDK和Eclipse

热门文章

  1. git中一些常见问题的解决
  2. 分布式事务与Seate框架(2)——Seata实践
  3. Beta设计和计划 —— NameNotFound
  4. auto_increment 自增长
  5. Envoy:离群点检测 outlier detection
  6. [bug] CDH 安装 Error : No matching Packages to list
  7. Linux进阶之日志管理
  8. MyBatis 各种参数传递方式
  9. spark_shuffle方式的演进过程
  10. linux上传启动项目命令