Problem A 游戏

有$n (1 \leq n \leq 5)$个硬币初始以"0"(正面),"1"(反面) $m (1 \leq m \leq m)$种操作,每一种操作可以将一些硬币进行翻转。

但是有$ k (0 \leq k < \frac{(m-1)\times m}{2}$) 组限制,每组限制描述$A$操作和$B$操作互不能连续进行。

已知翻转游戏进行$T$轮,求$T$轮之后,硬币全部翻转成反面朝上的方案数。

对于100%的数据 $1 \leq T \leq 10^5$

对于真正的100%的数据 $1 \leq T \leq 10^9 $

Sol : 对于100%的数据发现T的值比较小,暴力状压DP即可。

  设 $f_{i,j,k}$ 表示当前是第 $i$ 轮末,当前硬币的状态是 $j$ ,上一次转移而来的翻转操作的编号为 $k$。

  转移方程为$f_{i,j,k} = \sum\limits_{j'=0,k'=1 }^{j' \leq 2^n-1 , k' \leq m} f_{i-1,j',k'}$,

  其中限制是$j'$可以通过操作$k$变为$j$,并且操作$k$和操作$k'$可以连续进行。

  所以,这种算法的时间复杂度是$O(T\times 2^n m n )$,可以AC。

# pragma GCC optimize()
# include <bits/stdc++.h>
using namespace std;
const int mo=1e9+;
int a[],n,m,k,T,tmp[];
vector<int>v[];
int f[][][];
int main()
{
scanf("%d%d%d",&n,&m,&k);
int start=;
for (int i=;i<=n;i++) {
int t; scanf("%d",&t);
start=(start<<)+t;
}
for (int i=;i<=n;i++)
for (int j=;j<=n;j++)
v[i].push_back(j);
for (int i=;i<=m;i++) {
int t; scanf("%d",&t);
memset(tmp,,sizeof(tmp));
for (int j=;j<=t;j++) { int o; scanf("%d",&o); tmp[o]=;}
int rr=;
for (int j=;j<=n;j++) rr=(rr<<)+tmp[j];
a[i]=rr;
} for (int i=;i<=k;i++) {
int x,y; scanf("%d%d",&x,&y);
for (int j=;j<v[x].size();j++)
if (v[x][j]==y) v[x].erase(v[x].begin()+j);
for (int j=;j<v[y].size();j++)
if (v[y][j]==x) v[y].erase(v[y].begin()+j);
} memset(f,,sizeof(f));
for (int i=;i<=m;i++) {
f[][start xor a[i]][i]=;
} scanf("%d",&T);
for (int i=;i<=T;i++)
for (int j=;j<=(<<n)-;j++)
for (int k=;k<=m;k++)
for (int t=;t<v[k].size();t++) {
int w=v[k][t];
f[i][j][k]=(f[i-][j^a[k]][w]+f[i][j][k])%mo;
}
int ans=;
for (int i=;i<=m;i++) ans=(ans+f[T][(<<n)-][i])%mo;
printf("%d\n",ans); return ;
}

Game_100.cpp

  而对于真正100%的数据则要使用矩阵加速DP,考虑到状态$(j',k')$可以变成$(j,k)$的条件是$j'$可以通过操作$k$变为$j$,并且操作$k$和操作$k'$可以连续进行。

  所以我们可以预处理出一个矩阵表示从某一个状态的二元组到另外一个状态的二元组的转移是否可行,如果可行置为1,不可行置为0.

  如何在普通的矩阵中表示一个转移的二元组呢?可以Hash一下,把二元组$ (j,k) $表示成$ j\times m + k$即可完成转移。

  实现方面,先预处理出T=1时的情况(不存在转移的合法性)作为初始矩阵,然后若转移$(j',k')$到$(j,k)$合法,那么就将$A[j'm+k'][jm+k]$置为1,否则置为0.

  然后对于矩阵A计算$A^{T-1}$表示,这个相同的转移方式一共进行T-1次,获得的矩阵再乘以初始矩阵就是最终的答案矩阵$Ans$了,

  最终的答案只需对$Ans$矩阵每个元素求和即可。

  复杂度是$O(log_2 T (2^n m + k)^3 )$足以通过$T\leq 10^9$的数据。

#include <fstream>
#include <algorithm>
using namespace std; ifstream in("game.in");
ofstream out("game.out"); const int N = ;
const long long P = ;
const int M = ; int operatorlist[M];
int n, m, t, s;
int start_bit = ;
int ban[M][M];
long long tmp[N][N];
long long result[N][N]; void matrix_multiplication(int n, long long A[N][N], long long B[N][N])
{
int C[N][N];
for (int i=; i<n; i++)
for (int j=; j<n; j++)
{
C[i][j] = ;
}
for (int i=; i<n; i++)
for (int j=; j<n; j++)
for (int k=; k<n; k++)
{
C[i][j] =(C[i][j]+(A[i][k]*B[k][j] % P))%P;
}
for (int i=; i<n; i++)
for (int j=; j<n; j++)
{
A[i][j] = C[i][j];
}
} void matrix_addition(int n, long long A[N][N], long long B[N][N])
{
for (int i=; i<n; i++)
for (int j=; j<n; j++)
{
A[i][j]=(A[i][j]+B[i][j]) % P;
}
} void input()
{
in>>n>>m>>t;
for (int i=; i<n; i++)
{
int x;
in>>x;
start_bit = (start_bit<<)+x;
}
for (int i=; i<m; i++)
{
int k, op = ;
in>>k;
for (int j=; j<k; j++)
{
int x;
in>>x;
op += <<(n-x);
}
operatorlist[i+] = op;
}
for (int i=; i<t; i++)
{
int x, y;
in>>x>>y;
ban[x][y] = ;
ban[y][x] = ;
}
in>>s;
}
void work()
{
for (int i=; i<(<<n); i++)
for (int j=; j<m; j++)
for (int k=; k<m; k++)
if (ban[j+][k+]!=)
{
int next_i = i^operatorlist[k+];
tmp[i*m+j][next_i*m+k] = ;
}
int nn = (<<n)*m;
bool first = true;
s-=;
while (s>)
{
if (s&)
{
if (first){
matrix_addition(nn, result, tmp);
first = false;
}else
{
matrix_multiplication(nn, result, tmp);
} }
matrix_multiplication(nn, tmp, tmp);
s>>=;
}
} void output()
{
long long ans =;
for (int i=; i<m; i++)
for (int j=; j<m; j++)
{
ans = (ans + result[(start_bit^operatorlist[j+])*m+j][((<<n)-)*m+i]) % P;
}
out<<ans<<endl;
in.close();
out.close();
} int main()
{
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
input();
work();
output();
}

Game_100.cpp

 Problem B  探险

  给出一棵含有$n$个节点的树,和$m$条路径$s_i,t_i,d_i$完成下列两个任务。

  Rask1 : 对于每一条路径输出不在路径上的节点最小点的编号,(强制在线),

  Rask2:询问是否存在一个点$u$使得$u$到达路径$i$的距离(距离定义为点到路径的最短距离)都小于等于$d_i$ (要求输出"YES"和"NO")

  对于100%的数据$1 \leq n,m \leq 10^5$

Sol : 对于第一问,对每个节点都在其父亲的基础上建立可持久化线段树维护从根节点到每个节点路径上的编号在值域区间出现的总次数。

   对于一条路径$s_i , t_i ,d_i$求出$s_i,t_i$的LCA为$l_i$而$l_i$的直接父亲为$L_i$ ,

  由于每棵线段树的结构相同,所以在遍历线段树查询的时候,直接对$s_i$节点维护线段树+$t_i$节点维护的线段树-$L_i$-$l_i$节点维护的线段树的值域进行查找即可。

  这本质上用权值线段树对值域进行的二分查找。复杂度$O(n log_2 n)$

  对于第二问,贪心求解,对于每一条路径$s_i,t_i,d_i$求出$s_i,t_i$的LCA$l_i$,然后在$l_i$上跳$d_i$条边所得得节点设为$r_i$,如果不存在(跳到根节点以上)则不考虑。

  记深度最大的$r_i$节点为$R$,那么$R$是最优选取的点$u$,(其他可能的$u$可行的$R$一定可行,$R$若可行,其他可能节点$u$不一定行)。

  我们对上述贪心结论可以做出如下证明: (若 $R$节点对$s_i , t_i , d_i$不可行)

    1. 如果 $s_i , t_i $ 在$R$所在的子树中,那么其上跳$d_i$ 后若不可到达$R$ (由于其不可行) ,而与R的深度最大性不符合,不可能。

    2.如果 $s_i ,t_i $有$1$个节点在$R$所在子树中,那么路径必然经过节点$R$,所以其距离应该是$0$,而$d_i \geq 0$是必然的,不符合,不可能。

    3.根据1,2两条结论我们可以发现,$s_i , t_i $必然不在$R$的子树中, 为了联系这个探险家,这个节点必然需要到$R$的子树外,所以在$R$的子树内的某一个探险家就无法被联系,不可能。

  综上所述,$R$必然是最优节点,所以我们只需要计算$R$与每条路径的距离即可。

  如果$R$被某一条路径经过,那么$R$到这条路径的距离是$0$ 否则就是$R$到$lca(s_i,t_i)$的距离。

 $Update : $怎么判断一个点$x$在树的路径$(u,v)$上呢?

 只要其$(dep_x \geq dep_{lca}) \ And \ (LCA(u,x) = R) \ And \ (u$向上跳$dep_u - dep_x $步是$x)$ 或 $(dep_x \geq dep_{lca}) \ And \ (LCA(v,x) = R) \ And \ (v$向上跳$dep_v - dep_x $步是$x)$ 即可

  上述的两个问题总时间复杂度为$O(m log_2 n )$即可完成。

  Ps : 人傻自带大常数。

#pragma GCC optimize(3)
# include<bits/stdc++.h>
using namespace std;
const int N=5e5+;
const int B=;
struct Path { int s,t,lca,d; }path[N];
struct Segment_Tree{ int ls,rs,cnt;}t[N*];
struct rec{ int pre,to;}a[N<<];
int root[N*],g[N][],head[N],dep[N];
int n,m,tot,size;
int R,valR;
inline int read()
{
int X=,w=; char c=;
while(c<''||c>'') {w|=c=='-';c=getchar();}
while(c>=''&&c<='') X=(X<<)+(X<<)+(c^),c=getchar();
return w?-X:X;
}
inline void write(int x)
{
if (x>) write(x/);
putchar(x%+'');
}
void clear()
{
tot=; size=; R=; valR=; root[]=;
memset(head,,sizeof(head));
memset(t,,sizeof(t));
memset(a,,sizeof(a));
}
void adde(int u,int v)
{
a[++tot].pre=head[u];
a[tot].to=v;
head[u]=tot;
}
#define lson t[rt].ls,l,Mid
#define rson t[rt].rs,Mid+1,r
#define Mid ((l+r)>>1)
void insert(int last,int &rt,int l,int r,int val)
{
rt=++size; t[rt]=t[last];
if (l==r) {t[rt].cnt++;return;}
if (val<=Mid) insert(t[last].ls,lson,val);
else insert(t[last].rs,rson,val);
t[rt].cnt=t[t[rt].ls].cnt+t[t[rt].rs].cnt;
}
int query(int ru,int rv,int rlca,int rfa,int l,int r)
{
if (l==r) return l;
int ret=t[t[ru].ls].cnt+t[t[rv].ls].cnt-t[t[rlca].ls].cnt-t[t[rfa].ls].cnt;
if (Mid-l+>ret) return query(t[ru].ls,t[rv].ls,t[rlca].ls,t[rfa].ls,l,Mid);
else return query(t[ru].rs,t[rv].rs,t[rlca].rs,t[rfa].rs,Mid+,r);
}
void dfs(int u,int fath)
{
g[u][]=fath; dep[u]=dep[fath]+;
insert(root[fath],root[u],,n,u);
for (int i=head[u];i;i=a[i].pre) {
int v=a[i].to; if (v==fath) continue;
dfs(v,u);
}
}
int LCA(int u,int v)
{
if (dep[u]<dep[v]) swap(u,v);
for (int i=;i>=;i--)
if (dep[g[u][i]]>=dep[v]) u=g[u][i];
if (u==v) return u;
for (int i=;i>=;i--)
if (g[u][i]!=g[v][i]) u=g[u][i],v=g[v][i];
return g[u][];
}
void init() {
for (int i=;i<=;i++)
for (int j=;j<=n;j++)
g[j][i]=g[g[j][i-]][i-];
}
void jump(int u,int d)
{
if (dep[u]-dep[]<d) return;
for (int i=;i>=;i--)
if (d>=(<<i)) d-=(<<i),u=g[u][i];
if (dep[u]>valR) valR=dep[u],R=u;
}
int jumpto(int u,int d)
{
if (d<) return -;
for (int i=;i>=;i--)
if (d>=(<<i)) d-=(<<i),u=g[u][i];
return u;
}
int dis(int u,int v)
{
int lca=LCA(u,v);
return dep[u]+dep[v]-*dep[lca];
}
int main()
{
int T=read();
while (T--) {
clear();n=read();m=read();
for (int i=;i<n;i++) {
int u=read(),v=read();
adde(u,v); adde(v,u);
}
dfs(,); init();
for (int i=;i<=m;i++) {
int s=read(),t=read(),d=read();
int lca=LCA(s,t); jump(lca,d);
int ans=query(root[s],root[t],root[lca],root[g[lca][]],,n);
write(ans);putchar('\n');
path[i].s=s; path[i].t=t; path[i].lca=lca; path[i].d=d;
}
bool check=true;
for (int i=;i<=m;i++) {
int u=path[i].s,v=path[i].t,lca=path[i].lca,d=path[i].d;
if (dep[R]>=dep[lca]&&LCA(u,R)==R&&jumpto(u,dep[u]-dep[R])==R) continue;
if (dep[R]>=dep[lca]&&LCA(v,R)==R&&jumpto(v,dep[v]-dep[R])==R) continue;
if (dis(R,lca)>d) { check=false; break; }
}
if (check) putchar('Y'),putchar('E'),putchar('S'),putchar('\n');
else putchar('N'),putchar('O'),putchar('\n');
}
return ;
}

explore.cpp

Problem C 或

T组询问,给出两个01串$a_i$和$b_i$,要求出一个$c_i$,

若在$a_i$和$b_i$中存在$a_x = 1$,$b_y = 1$,并且$x or y = i$,那么$c_i = 1$否则$c_i = 0$

对于100%的数据$1 \leq |a| = |b| \leq 2\times 10^5$

Sol:  第一次接触二进制高维前缀和。

  我们对$i \in [1,n]$分别考虑,如果$i$的二进制位表达$I$可以拆分成两个子集$A$和$B$并且使得$A \cup  B = I$

  并且以二进制表达的A表示的十进制数作为下标$i$在$a_i$中不为0,并且以二进制表达B表示的十进制数作为下标的$j$在$b_j$中也不为0。

  那么$c_i = 1$否则$c_i = 0$ ,这是对原来题目的一步转化。

  为了判断当前位$i$是否合法,我们需要知道$s_i=\sum_{J\sqsubseteq I} a_j$ 其中$I$表示$i$的二进制表示,$J$表示$j$的二进制表示。

  为了数学严谨上述公式可以被严谨的表示为$ s_i = \sum _{j \ \& \ i \ = \ i} a_j $

  我们如何对于上述定义下 某一数组$a_i$的对应的$s_i$数组,称为a的子集和。

  我们可以把a数组下标$i$当做二进制位拆分,一个“01”位表示一维,那么$s_i$就是$a_i$的$log_2 n $维前缀和。

  于是可以这么处理,先从低到高枚举维度$i$,然后枚举每一个下标$j$,如果该下标的第$j$位是$1$那么就是$i-1$维度$j$第$i$位是0或1两种情况的和。

  如果该第$j$位是$1$那么就是$i-1$维度$j$第$i$位是0一种情况的和。

  上面是基于一个很简单的DP,在实现过程中我们可能会记$f_{i,j}$表示第$i$维度第$j$位置的前缀和,但是我们很快就发现$i$的记录可以被滚动。

  于是就可以简单的用如下代码计算(每一次j循环开始的时候都是i-1维度时的前缀和,0维前缀和是原数列)

for i =  -> log_2 n+ i++
for j = -> n j++
if (j 第 i 位 是 ) a[j] = a[j] + a[去掉第 i 位的 1后的十进制下标];
else a[j] = a[j]

  我们回到原来的问题,对于$a_i$数组和$b_i$数组分别求出其子集和$s_1,s_2$,令$c_i = a_i \times b_i $那么$c$就是要求$c$方案总数的高维前缀和;

  然后对$c$做高维差分(就是高维前缀和逆运算)之后就是原来$c$的值,其含义是$c_i = 1$的取值$(x,y)$的组数。

  如果$c_i = 0 $输出0否则输出1即可。

  复杂度$O(T n log_2 n)$

# include<bits/stdc++.h>
# define int long long
using namespace std;
const int N=3e5+;
char s[N];
int a[N],b[N],c[N];
signed main()
{
int T; scanf("%d",&T);
while (T--) {
scanf("%s",s+); int n=strlen(s+);
for (int i=;i<=n;i++) a[i]=s[i]-'';
scanf("%s",s+);
for (int i=;i<=n;i++) b[i]=s[i]-''; int t=log(n)/log()+; for (int i=;i<=t;i++)
for (int j=;j<=n;j++)
if (j&(<<i)) a[j]+=a[j^(<<i)]; for (int i=;i<=t;i++)
for (int j=;j<=n;j++)
if (j&(<<i)) b[j]+=b[j^(<<i)]; for (int i=;i<=n;i++) c[i]=a[i]*b[i]; for (int i=t;i>=;i--)
for (int j=;j<=n;j++)
if (j&(<<i)) c[j]-=c[j^(<<i)]; for (int i=;i<=n;i++) printf("%d",c[i]>);
puts("");
} return ;
}

or.cpp

HGOI20190710 题解的更多相关文章

  1. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  2. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  3. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  4. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  5. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  6. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  8. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

  9. CF100965C题解..

    求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...

随机推荐

  1. 面向服务架构之RPC原理与实例

    1.RPC概述 RPC(Remote Procedure Call)即远程过程调用,允许一台计算机调用另一台计算机上的程序得到结果,而代码中不需要做额外的编程,就像在本地调用一样.主要是为了应对当前互 ...

  2. Linux软链接硬链接的区别

    ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在 ...

  3. PHP 时间转几分几秒

    public static function timetodate($c){ if($c < 86400){ $time = explode(' ',gmstrftime('%H %M %S', ...

  4. 在Qt5使用中文(vs环境)

    如果是使用mingw版本的Qt create, 也就是使用GCC编译器应该没那么多事吧. 不过我还是用惯了VS呢. 好了,废话不多说,开始总结vs下乱码的解决方案. vs2003 把源码存成 utf- ...

  5. tensorflow 使用tfrecords创建自己数据集

    直接采用矩阵方式建立数据集见:https://www.cnblogs.com/WSX1994/p/10128338.html 制作自己的数据集(使用tfrecords) 为什么采用这个格式? TFRe ...

  6. idea自定义注释

    类配置位置: 方法配置位置 配置内容 * * @Author *** * @Date $date$ $time$ $param$ * @return $return$ * @Description * ...

  7. js制作留言板

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. 关于解决SpringDataJpa框架实体类表字段创建顺序与数据库表字段展示顺序不一致的问题

    今天在公司的项目开发中,遇到一个问题: 后端对象实体类中写入字段顺序与数据库中的存储顺序不一致. 仔细观察到数据库中的表字段的排序方式是按照拼音字母的顺序abcdef......来存储的 而我的实体类 ...

  9. Maven中setting.xml 配置详解

    文件存放位置 全局配置: ${M2_HOME}/conf/settings.xml 用户配置: ${user.home}/.m2/settings.xml note:用户配置优先于全局配置.${use ...

  10. 红色警戒2CE修改教程

    在大学的时候特别喜欢玩游戏,尤其偏爱单机游戏.在玩一些单机游戏的时候,特意使用了一些修改工具.本来是打算做成一个系列的,但是现在由于时间问题,仅介绍一些.(大概包括rimworld,饥荒,放逐之城,缺 ...