FJ省队集训最终测试 T3

思路:状态压缩dp,f[i][j[[k]代表i行j列这个格子,连续的状态为k,这个连续的状态是什么?就是下图

X格子代表我当前走到的地方,而这里的状态就是红色部分,也就是连续的一段n的状态,我们是分每一位计算的,这样就可以转移了,注意,当当前点在最下面的时候要额外计算一个与1的贡献。

坑爹,inf设小了只有30分。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define ll long long
 7 const ll inf=1e18;
 8 ll f[50005][41],bin[55],sum[55],ans;
 9 ll a[6][20005],b[6][20005],c1[6][20005],c2[6][20005];
10 ll s[220005];
11 int n,m;
12 ll read(){
13     ll t=0,f=1;char ch=getchar();
14     while (ch<‘0‘||ch>‘9‘){if (ch==‘-‘)f=-1;ch=getchar();}
15     while (‘0‘<=ch&&ch<=‘9‘){t=t*10+ch-‘0‘;ch=getchar();}
16     return t*f;
17 }
18 ll query(int id,int j,int st){
19     int top=0;
20     for (int i=1;i<=n;i++){
21         s[++top]=st%2;
22         st/=2;
23     }
24     s[0]=s[top];s[top+1]=s[1];
25     ll res=0;
26     for (int i=1;i<=n;i++)
27      res+=((s[i]*bin[id])^(a[i][j]&bin[id]))*b[i][j];
28     for (int i=1;i<=n;i++)
29      res+=((s[i]*bin[id])^(bin[id]*s[i+1]))*c2[i][j];
30     return res;
31 }
32 void dp(int id){
33     int tot=(1<<n)-1,cnt=0;
34     for (int i=1;i<=n*m;i++)
35      for (int j=0;j<=tot;j++)
36       f[i][j]=inf;
37     for (int i=0;i<=tot;i++)
38      f[n][i]=query(id,1,i);
39     for (int j=2;j<=m;j++)
40      for (int i=1;i<=n;i++){
41         int now=(j-1)*n+i,pre=now-1;
42         for (int st=0;st<=tot;st++){
43             int st1=st&sum[n-2],lst=((st&bin[n-2])>0),ths=((st&bin[n-1])>0);
44             ll tmp=0;
45             if (i!=1) tmp+=((lst^ths)*c2[i-1][j])*bin[id];
46             if (i==n) tmp+=(((st&bin[0])^ths)*c2[i][j])*bin[id];
47             tmp+=((a[i][j]&bin[id])^(ths*bin[id]))*b[i][j];
48             for (int k=0;k<=1;k++){
49                 int st2=st1*2+k,beh=((st2&bin[0])>0);
50                 ll tmp2=(beh^ths)*bin[id]*c1[i][j-1];
51                 f[now][st]=std::min(f[now][st],f[pre][st2]+tmp+tmp2);
52             }
53         }
54      }
55      ll Tmp=inf;
56      for (int i=0;i<=tot;i++)
57       Tmp=std::min(f[n*m][i],Tmp);
58      ans+=Tmp;
59 }
60 int main(){
61     n=read();m=read();bin[0]=1;
62     for (int i=1;i<=19;i++) bin[i]=bin[i-1]*2;sum[0]=bin[0];
63     for (int i=1;i<=19;i++) sum[i]=sum[i-1]+bin[i];
64     for (int i=1;i<=n;i++)
65      for (int j=1;j<=m;j++)
66       a[i][j]=read();
67     for (int i=1;i<=n;i++)
68      for (int j=1;j<=m;j++)
69       b[i][j]=read();
70     for (int i=1;i<=n;i++)
71      for (int j=1;j<m;j++)
72       c1[i][j]=read();
73     for (int i=1;i<=n;i++)
74      for (int j=1;j<=m;j++)
75       c2[i][j]=read();
76     for (int i=0;i<=19;i++){
77         dp(i);
78     }
79     printf("%I64d\n",ans);
80     fclose(stdin);fclose(stdout);
81     return 0;
82 }
时间: 2024-10-03 18:24:39

FJ省队集训最终测试 T3的相关文章

FJ省队集训最终测试 T2

思路:发现如果一个人一共选了x个点,那么选中某一个点对的概率都是一样的,一个人选x个点的总方案是C(n,x),一个人选中某个点对的总方案是C(n-2,x-2),这样,那么选中某个点对的概率就是 x*(x-1)/(n*(n-1)),这样,我们就用树分治求出有多少对符合条件的对数,然后乘上每个人的概率即可. 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring>

FJ省队集训DAY2 T2

思路:我们可以考虑三角剖分,这样问题就变成考虑三角形的选取概率和三角形内有多少个点了. 先用树状数组预处理出三角剖分的三角形中有多少个点,然后用线段树维护,先用原点极角排序,然后枚举i,再以i极角排序,此时线段树的作用就来了,每次到一个询问的教室点,我们就在线段树里面查找之前的概率,统计贡献即可. 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #

FJ省队集训DAY1 T1

题意:有一堆兔子,还有一个r为半径的圆,要求找到最大集合满足这个集合里的兔子两两连边的直线不经过圆. 思路:发现如果有两个点之间连边不经过圆,那么他们到圆的切线会构成一段区间,那么这两个点的区间一定会有交集,形如s0 s1 e0 e1 同样的,如果是n个点,那就是s0 s1 s2..sn e0 e1 e2.. en 因此,我们枚举那个起始点,然后对于其他点我们按照s排序,对于e做最长上升子序列即可.时间复杂度O(n^2 logn) 1 #include <cstdio> 2 #include

FJ省队集训DAY3 T1

思路:我们考虑如果取掉一个部分,那么能影响到最优解的只有离它最近的那两个部分. 因此我们考虑堆维护最小的部分,离散化离散掉区间,然后用线段树维护区间有没有雪,最后用平衡树在线段的左右端点上面维护最小的id 我讲的貌似不是很清楚.. 还有,蜜汁80分,打死也改不出来.. 1 #include<cstdio> 2 #include<cmath> 3 #include<iostream> 4 #include<cstring> 5 #include<algo

FJ省队集训DAY2 T1

思路:转换成n条三维空间的直线,求最大的集合使得两两有交点. 有两种情况:第一种是以某2条直线为平面,这时候只要统计这个平面上有几条斜率不同的直线就可以了 还有一种是全部交于同一点,这个也只要判断就可以了. 然后我并不能改出来,wa了好多个点 WA的程序: 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm&

FJ省队集训DAY5 T1

思路:考试的时候打了LCT,自以为能过,没想到只能过80.. 考完一想:lct的做法点数是100W,就算是nlogn也会T. 讲一下lct的做法把:首先如果一条边连接的两个点都在同一个联通块内,那么这条边对答案没有影响,可以忽略,因此,问题变成了每次询问两个点中路径上权值最大的边(这里的权值我们令它为加入这条边的时间),边我们用一个点连接两个端点来表示. 正解:由于是无根树,因此我们用并查集按秩合并,每次把小的加到大的里面去,询问的时候暴力走lct查找最大即可. 1 #include<cstdi

FJ省队集训DAY4 T2

XXX 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<vector> 7 using namespace std; 8 typedef unsigned long long ll; 9 ll a,mod=1,L=1; 10 ll tr[4],b[4],tmp[4

FJ省队集训DAY4 T1

直接上题解 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #define ll long long 7 const int Mod=1000000009,N=3000; 8 ll jc[N+10],jcny[N+10],jcnys[N+10],K[N+10],p[N+10],f[N+10];

FJ省队集训DAY3 T2

思路:如果一个DAG要的路径上只要一条边去切掉,那么要怎么求?很容易就想到最小割,但是如果直接做最小割会走出重复的部分,那我们就这样:反向边设为inf,这样最小割的时候就不会割到了,判断无解我们直接用tarjan 1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #define ll long