08 2012 档案
摘要:为了学习高斯消元写了下这一题,代码是按照维基百科的伪代码来写的,每个点的状态和他自己以及周围五个点是否按下有关,于是只要每个点作为一个未知数,列出30个方程,用高斯消元求解即可。 其实这题完全可以不必用高斯消元,枚举第一行的状态就可以了,第一行的状态可以决定第二行的状态,进而就可以决定整幅图的状态。 1 #include <string.h> 2 #include <stdio.h> 3 #include <algorithm> 4 #include <stdlib.h> 5 int cas,n,m,x[50][50],ans[31]; 6 vo
阅读全文
摘要:有三个面数不同的骰子,每次得分为三个骰子的点数和,但当三个骰子的点数满足某个条件时,分数会清0,问分数大于N需要投掷次数的期望。 典型的期望题,但是有环。论文中说这种题一般用高斯消元通解,但是这一题比较特殊,所有的环都是回到0点,所以可以将每个点的期望化简到一个关于E[0]的线性方程租,然后求系数即可。 1 /* 2 设E[x]为x分时到达n分的期望投掷次数,pk为掷到k分的概率,p0为回到0分的概率 3 有E[x]=∑E[x+k]*pk+p0*E[0]+1 4 假设E[x]=A[x]*E[0]+B[x] 5 则 E[x]= ∑(A[x+k]*E[0]+B[x+k])*p[k]+p...
阅读全文
摘要:一个连通的图上有一只猫和老鼠,猫每次能移动一步,会选择到老鼠的最短路去移动,如果有多个选择,移动到标号最小的一个点。如果一步没有抓到老鼠的话,猫可以连续继续移动一步。接着老鼠会随机移动到他附近的一个点或者留在原地,每种移动的概率是1/(该点的度+1)。求猫平均几次可以抓到老鼠。 p[i][j]表示猫在i老鼠在j猫下一步会选择的点,对每个点bfs一遍可以预处理出来。f[i][j]表示猫在i,老鼠在j猫抓到老鼠的期望,则有 f[i,i]=0,p[i,j]==j或者p[p[i,j],j]==j时f[i][j]=1。 1 #include <string.h> 2 #include <
阅读全文
摘要:为了理解期望DP做的第一道期望题。 一共有s个子系统和n个bug,一次能找一个Bug,求出在s个子系统中发现n个Bug的期望次数。bug是无穷的,所以认为每次在每个系统找找到一个Bug的概率是相等的。 这种题目基本都是设E(now)为从当前状态到达结束状态的期望,E(now)=∑E(x)p(x),x表示可以从now转移到的状态,p[x]表示转移到这个状态的概率。 1 /* 2 s个子系统,n个bug, 3 e[i][j],在i个子系统中发现j个bug到达目标的期望,显然d[s][n]=0; 4 e[i][j]可以转化成四种情况 5 发现无用的bug e[i][j...
阅读全文
摘要:看了大牛的代码,在斌牛的指导下,终于AC了这题。 题目很短,就是问一个由大写字母组成的矩阵中有多少个不同的子矩阵。 从1到m枚举宽度,对每个宽度进行HASH,hash[i][j]表示s[i][j...j+width-1]这个串的hash值,然后再将hash值按照hash[i][0],hash[i+1][0]..hash[n-1][0],#,hash[i][1]...hash[n-1][1],这样竖着的顺序连接起来。并在每一列的串之间用一个符号隔开,这样形成了一个串,再求这个串的不重复子串有多少个,最后将所有宽度的不重复子串和加起来就可以了。这个应该比较容易理解,当枚举宽度为width时...
阅读全文
摘要:Continuous Same Game (2)是个很BT的搜索,这题是为了做(2)才来做一下的。其实直接模拟很easy的,纯属无聊写了个位运算版本的,感觉效率似乎差不多。。就是用3位表示每一位的状态,这样一个longlong就可以存下一列的状态。 1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 typedef long long LL; 5 LL d[22],nfull[22],full[22]; 6 int di[]={-1,1,0,0},dj[]={0,0,1,-1}
阅读全文
摘要:维护两个字符串,操作一可以将其中一个字符串的一个字符进行修改,操作二是查询从某一位开始的最长公共串。 这题比较简单,只要维护一个数组,0表示两个字符串该位字符相同,1表示两个字符串该位字符不同。用树状数组维护前缀和,用sum(i)表示前i个元素的和,如果要查询从第i位开始的最长公共串,只要求满足sum(x)-sum(i)==0的最大x即可,二分去找这个x就行了。复杂度比线段树去做多一个二分的log(N),但是线段树常数比树状数组大不少,所以实际效率是差不多的,而且代码要好写很多。 1 #include <stdio.h> 2 #include <string.h> 3
阅读全文
摘要:题目大意是说给你一个起点和终点,一个人要从起点走到终点,它不能经过一个点两次,问他不可能经过哪些点。 显然转化成能经过哪些点要好想一些,用N减去能经过的点就可以得到答案。下面的讨论都是基于求他可能经过的点有多少个。 很容易想到用双联通分量,但是建图确实比较麻烦。如下图,一个人想要从1走到3,那它可能在的点就是1,2,3。因为2是一个割点,它如果从2走到了4,想要到达3就必须再经过2,所以可以用割点和双联通建图。 用割点和双联通可以建成双联通与割点相邻的图,若右图所示。可以证明这是一棵树,因为如果存在环,这个环必然可以缩成一个点,树中任意两点有且仅有一条路径,于是只要统计这条路径上有一...
阅读全文
摘要:题目基于一个定理:当所有点相连的点的个数大于N/2时,一定可以构造出一条哈密顿回路,构造的复杂度是N^2。 这题的正解是构造,但很多人都是搜索过的,只能说明这题的数据比较水。sgu上有一道基本一样的题目(sgu122),这一题不用构造是过不了的。 至于构造方法,也就是3步。 STEP1:先找两个相邻点s-t,扩充出一条链直到到不能扩充为止。 STEP2:这时如果s-t不相邻,在链上找一点vi满足s与next[vi]相邻并且vi与t相邻,链转化为s-vi-t-next[vi]-s,根据鸽巢原理这个点必然存在。 STEP3:如果链中的点不足N个,就在环上开一个新口继续加点,重复STE...
阅读全文
摘要:题解说是容斥原理,彻底没看懂,搜到的题解也是各种简洁,就提到是容斥原理,难道大家的数学都这么好,一眼就看懂了。。。。 这题也可以用概率DP做,相比之下这种做法要容易理解的多。用一个状态表示当前抽到的卡片的状况,1代表尚未拿的卡片,有d[now]=x*d[now]+sigma(si*pi*(now^(1<<i)))+1,si表示stat中该位是0还是1,x表示停留在该状态的概率,即没拿到其它卡片的概率(没抽到卡片+抽到当前已有卡片),移项可以得到d[now]=sigma(..)/(1-x)。 1 #include <string.h> 2 #include <std
阅读全文
摘要:这就是大神口中的简单题,我的数论简直烂到不行,这些数学出身的家伙何必出这些恶心的数学题来难为我们呢。。 用到一个公式 A^x%P=(A^(x%phi(P)+phi[P]))%P (x>=phi[C]),phi[C]表示欧拉函数。 注意条件是x>=phi[C],但这题的x是增长很快的,很快就能满足这个条件,所以x<phi[C]这部分暴力即可。 接着继续暴力求x%phi[C]!=0的情况,很快就会出现x%phi[C]==0,并且之后一直都会满足,题目转化为求A^phi[P]%P==b,根据鸽巢原理,一定会出现长度为P循环节,于是统计循环节内满足条件的数的个数,再乘以循环节个数,然
阅读全文
摘要:很老的题了,一开始懒的写HASH写个二分查找还没过,后来改成HASH就A了。。 1 #include <stdio.h> 2 #include <string.h> 3 #define MOD 999997 4 #define PRI 199 5 typedef __int64 LL; 6 LL add=10000000000000000LL; 7 int cas,n; 8 LL d[5][201]; 9 LL num[MOD];10 int first[MOD],next[MOD/2],es;11 void ins(LL x,int p){12 num[es]=x,n
阅读全文
摘要:其实是很裸的一道题,可能是因为几乎没用过扩展KMP,只想到用后缀数组,但这题的常数卡的很厉害,不用扩展KMP应该是过不了的。 扩展KMP能求出一个串所有后缀串(即s[i...len])和模式串的最长公共前缀。于是只要将这个串复制一遍,求出该串每个后缀与其本身的最长公共前缀即可,当公共前缀>=len时,显然相等,否则只要比较下一位就能确定这个串与原串的大小关系。 至于重复串的问题,只有当这个串有循环节的时候才会产生重复串,用KMP的next数组求出最小循环节,用长度除以最小循环节得到循环节个数,在将3个答案都除以循环节个数即可。 1 #include <string.h> 2
阅读全文
摘要:烟囱是一个3*3的中空构造,给定烟囱的高度,问用1*1*2的砖头搭成这个烟囱有多少种方法。 首先用一个8位二进制数表示某一层的状态。枚举上下两层的状态,判断这两个状态是否可以相邻,上一层必须将下层填满,但是上层自身可以有空格。当下层为0时,上层必须为1,表示放一个竖着的矩形块,因此必须满足up_stat|low_stat=255,上层中除了竖着的砖头,都是单个或连续个横放的砖头,所以必须满足up_stat&low_stat中的连续1都是偶数个。注意循环的问题,比如0的位置是1,7位置也是1,而实际上0和7是相邻着的。 相邻层的状态会构造出一个矩阵表示转化关系,为0时代表不能相邻...
阅读全文
摘要:首先能够构成正方形的两个对顶点肯定在同一条对角线上,可以想到依次处理每条对角线上的点。 对于每个点,预处理出它能到的四个方向的连续1最多有多少个,记为p_up,p_down,p_left,p_right,显然,决定某一个点向右下能延伸的多少只取决于p_down和p_right中的较小值,决定某一个点向左上能延伸到多少只取决于p_up和p_left的较小值。 对于每个点,记录它能到达的最右的x轴的绝对坐标,并记录它自身的y坐标。 统计一个点和它左上的点(在对角线上)能组成多少个正方形时,只要判断有多少个点向右能和它相交即可。比如统计5,只要记录1~4中有多少点向右能和红线交叉,显然...
阅读全文
摘要:很明显的双向广搜,每次从M的方向搜3步,从G的方向搜1步,然后判断是否走到对方已经走过的格子即可。至于魔王的判断,直接用曼哈顿距离判断就可以了。 1 #include <string.h> 2 #include <stdio.h> 3 #include <queue> 4 #include <stdlib.h> 5 #define MAXN 805 6 struct pnt{ 7 int x,y; 8 pnt(){}; 9 pnt(int _x,int _y):x(_x),y(_y){}10 }gst[2],st,en,q[2][MAXN*MAX
阅读全文
摘要:虽然数很大,但是这40个数的公倍数一共才4万多个。这里用到STL会方便很多,map[i]里存放了从1~i能组成的公倍数有哪些以及每个数有多少种组成方法。 预处理一遍打个表然后每次O(n)的遍历一遍,否则会超时。 #include <string.h>#include <stdio.h>#include <map>typedef __int64 LL;int n,cas;LL m,ans;std::map<LL,LL> mp[41];std::map<LL,LL>::iterator it;LL gcd(LL a,LL b){ retu
阅读全文
摘要:简单线段树,开根号开几次这个数就为1了,之后再对其开根号就没有意义了。当一个区间全部为0或者1时就没有必要再去跟新这个区间了。 #include <string.h>#include <stdio.h>#include <math.h>#define lson l,m,p<<1#define rson m+1,r,p<<1|1#define calm (l+r)>>1#define MAXN 100005typedef __int64 LL;int n,q,cas=1,tu,tv,tq;LL sum[MAXN<<
阅读全文
摘要:一开始看范围特别小就写搜索了。。。结果自己随便出了个4*4就过不了了。。 其实就是状态压缩DP,求哈密顿回路要多少条,只是要注意判断两点是否可以划线就可以了。 #include <stdio.h>#include <string.h>#include <algorithm>typedef __int64 LL;int n,m,tot;int mz[25],bet[25][25][25],full;int zero[16],zeros,hzero[25],lowb[65536];LL d[16][65536],ans;int cant(int st,int e
阅读全文
摘要:ai取值范围0~1,xi取值范围0~3,所以最后异或的结果不会超过两位二进制。所以可以用一个60位的数保存30个式子的值,然后m的范围是小于22,二分一下就可以了,4^11=4*10^6,结果存下来排个序然后二分找一下就可以了。 #include <stdio.h>#include <string.h>#include <algorithm>typedef __int64 LL;int cas,n,m,ai[35][30],si[30][4];LL ans,tmp[1<<22+2];int sum[1<<22+2];int size,
阅读全文
摘要:简单二分,最大射击距离是必然能达到的,只要在能到达最大距离的瞬间就立刻射击可以保证时间最短。 #include <string.h>#include <stdio.h>#include <math.h>double xx1,yy1,xx2,yy2,lx,ly,vd,vb,l;double sqr(double x){return x*x;}double getdis(double xx1,double yy1,double xx2,double yy2){ return sqrt(sqr(xx1-xx2)+sqr(yy1-yy2));}int canreac
阅读全文
摘要:看上去很复杂的一道博弈,其实只要稍微分析就会发现情况其实很少很简单。。 斌牛的解释十分详细,我就不赘述了。。http://www.cnblogs.com/staginner/archive/2011/09/10/2173317.html 这里采用数组存储顺序可以大大减少代码的复杂程度,incs存储的是占用这个格子是会给自己留下稳定占位的个数,-1代表给对手留下了一个稳定占位。 #include<stdio.h>#include<string.h>/* Alice:15>3~6>11~14>7~8>9~10>1 Bob :15>3~6&
阅读全文
摘要:一开始想的很复杂,其实只要离散化一下就行了,炸x的时候将对应的y都-1,然后统计对应的y的时候将对应的已经炸掉的点减去即可。 #include <stdio.h>#include <string.h>#include <algorithm>#define MAXN 100005struct pnt{ int x,y;}px[MAXN],py[MAXN];bool cmpx(const pnt& a,const pnt& b){ return a.x<b.x||a.x==b.x&&a.y<b.y;}bool cmpy
阅读全文
摘要:给出如下图形,其中23个格子填有1~23,移动规则与八数码相同,给定始终态,问是否可达。 首先八个角的数字是不可能移动出去的,所以先对八个角进行判断,如果为空格则和附近格子的数进行交换,问题转化为16数码问题。和八数码类似,可以用奇偶性来判断两个状态是否可达,不同的是,将一个数上下移动会影响的整体的奇偶性,一个很好的方法就是"弓"字行统计,这样移动后奇偶性就不会改变了。#include <stdio.h>#include <string.h>int cas,a[25],b[25];//八个顶点必须相同,若为0和相邻的置换int p1[]={0,1,2
阅读全文
摘要:最小树形图模版题,学习了一下最小树形图。 三步走,找最小入弧,找有向环,缩环为点。#include <string.h>#include <stdio.h>#include <stdlib.h>#define INF 0x3f3f3f3f#define MAXN 1001struct edge{ int u,v,w;}e[MAXN*MAXN];int n,x,y,z,es,k,tt;int pnt[MAXN][3];int dist(int p1,int p2){ int ret=0; for(int i=0;i<3;i++)ret+=abs(pnt[
阅读全文
摘要:给定一棵树,求出当x为根时y的最小儿子和最小后代各是多少。 一道中等的树形DP,首先以1为根进行DP可以求出每个节点的最小儿子和最小后代。只有当x为y的孩子时才需要讨论,否则直接输出前面DP的结果即可,这里画个图就可以看出来,下面只讨论x为y的孩子的情况。 如果y的最小儿子是x的祖宗节点,那这时该最小儿子已经变成了y的父亲,在选取最小儿子时不能在考虑这个点,应该换选次小儿子,所以每个点的次小儿子也要在DP时预处理出来,另外,这时y的原父亲在新树中已经变成了y的儿子,所以在选取最小儿子时要考虑父节点。至于最小后代,如果y不是根节点,那么最小后代必然是根节点1了,如果y节点是根节点,就先找...
阅读全文
摘要:以为是什么复杂的几何题,结果边长是平行于坐标轴的,一下子就变成大水题了。找了半天也没发现题目哪写了边长平行于坐标轴。。。 数据范围小,n次枚举*nlogn排序,暴力枚举就可以过。#include <stdio.h>#include <string.h>#include <algorithm>#define MAXN 1005int n,r;struct pnt{ int x,y;}p[MAXN],p2[MAXN];bool cmpx(const pnt& p1,const pnt& p2){ return p1.x<p2.x;}bool
阅读全文
摘要:水题。虽然求第K大值的方法有很多种,各种树都可以,但这题只要一个元素个数固定的堆就可以了。。。。 #include <stdio.h>#include <string.h>#include <queue>#define MAXN 1000000int n,k,num;char op[5];int main(){//freopen("test.in","r",stdin); while(scanf("%d%d",&n,&k)!=EOF){ std::priority_queue<
阅读全文
摘要:敌人会在图中加一条边,你的任务是摧毁一条边,使敌人所在块不连通,求最小的花费。 首先摧毁双联通分量中的边是没有意义的,这个很容易理解。于是对原图进行缩点,行成一棵树,敌人加某一条边后会形成一个环,摧毁这个环中的点是没有意义的,显然对于敌人来说连接叶子节点形成的环会最大。如果这个环不包含最小边,那么去掉最小边后图就不连通了,所以敌人加边后生成的环必然包括最小边。以这个最小边的两个顶点为根生成两棵树,敌人选取了某个叶子节点后,那么选择从这个叶子节点到根节点路径上的边去摧毁都是毫无意义的。对每个节点维护两个值,一个是以该节点为根节点的子树中最小边权,一个是除去这个包含最小边权的子树外的其它子树...
阅读全文
摘要:一道简单的二分。 #include <string.h>#include <stdio.h>#include <algorithm>#define MAXN 500005int l,n,m;int d[MAXN];int cant(int x){ int now=0,ans=0; for(int i=1;i<=n;i++){ if(d[i]-d[i-1]>x)return 1; if(d[i+1]-d[now]>x)now=i,ans++; } //最后一步要跳到终点 if(now<n)ans++; return ans>m..
阅读全文
摘要:树形背包的变形,要特殊考虑当用一个机器人遍历一棵子树的情况。 /*d[i][k]表示节点i用k个机器人探索的最小花费跟树型背包问题差不多d[u][k]=d[u][k-i]+d[v][i]+i*w表示给当前已遍历的子树以及父节点k-i个机器人,给当前正在遍历子树i个机器人注意也可以把k个机器人全部给已遍历子树,d[v][0]实际上相当于用一个机器人遍历完再走到父节点所以这种情况下d[u][k]=d[u][k]+d[v][0]+2*w*/#include <stdio.h>#include <string.h>#define MAXN 10005#define MAXK 1
阅读全文
摘要:问小于n的数里n/phi(n)最大的数是多少。 由欧拉函数的性质可得n/phi(n)=n/(n*(1-1/p1)*(1-1/p2)...*(1-1/pn))=1/((1-1/p1)*(1-1/p2)...*(1-1/pn)),其中p1~pn为n的质因数。 可见n的质因数越多,n/phi(n)的值越大,所以令n为连续素数的乘积即可。 #include <string.h>#include <stdio.h>struct bign{ int s[205],len; bign(){memset(s,0,sizeof s);len=1;} bign(char *str){*..
阅读全文
摘要:简单的类LIS问题,按长和宽排序然后DP。注意两个问题即可,一是要规定长的边为长,短边为宽,二是排序时要将要求较少的块放在后面。 #include <stdio.h>#include <string.h>#include <algorithm>#define MAXN 1005typedef __int64 LL;struct block{ int ai,bi,ci,di; bool operator <(const block& b)const{ if(ai!=b.ai)return ai<b.ai; if(bi!=b.bi)retur
阅读全文
摘要:比较恶心的一道题,主要在数位DP那里调试了好久。 题目大意:0~9可以用对应的BCD码表示,给出A~B之间的十进制数,将他们化成BCD码的形式。但有些01串是不能出现的,求能化成BCD码的数的个数。 先建图,然后将二进制转十进制,比如next[p][7]=next[p][0]->[1]->[1]->[1],如果后面这条路能走出来next[p][7]就存在,否则为-1。之后数位DP,求能走出的比X的小的数有多少种,这里要注意细节,很容易写错。求出后DP(B)-DP(A)就行了。 1 #include <stdio.h> 2 #include <string.h
阅读全文
摘要:一道纠结了好久的题,主要是因为一开始思路就想错了,还按着错误的思路想了好久。。。 题目大意就是要找一个最短的字符串,包含所有的合法单词并且不包含所有的非法单词。 题目中给的非法单词非常多,但是合法单词很少,很容易想到用状态压缩来做。我一开始想到的是把每个合法单词当作一个点,然后用哈密顿回路来做,想了半天才发现这个思路明显是错误的。。。。。。 正确的做法是把所有可以作为合法单词结尾的点选出来,BFS出每两点之间的距离,每个点都会有一个压缩状态标记这个点可以作为哪几个单词的结尾,然后DP就可以了。 d[state][v]表示最后一步在v时到达state这个状态需要的最小长度,方程为d[...
阅读全文
摘要:题意是说给你若干个单词,每个单词有一定的分数。如果一个字符串包含了某个单词就会得到该单词的分数,并且可以重复计算。让你输出一个长度不大于N并且分数最大的字符串,分数相等时选最短的,长度相等时选字典序最小的。 DP方程很容易想,就是最普通的自动机DP。麻烦的地方在于保证字典序,如果从上向下DP,要保存前缀,比较字典序每次要从开头开始比较,可以写出来但是比较麻烦。我是将模式串反过来建图的,这样等于从后向前生成字符串,分数相同时每次选择较小的字符就一定能生成字典序最小的字符串,但要注意当两字符相等时要向前比较直到可以分出字典序为止。 代码交上去跑了15ms,看statistic已经排在第五了...
阅读全文
摘要:还是统计串问题,只是分成了同一种串允许互相覆盖和不允许互相覆盖两种情况。 对于允许互相覆盖的情况,就是模版了。对于不允许覆盖的情况,只要记录下该串最后一次出现并且被统计的位置即可,能统计的条件是lasp[id]+len[id]<=pos。#include <string.h>#include <stdio.h>#define MAXL 600001#define MAXN 100001#define INF 0x3fffffffchar s[MAXN],s1[10];int n,type,stp[MAXN],sps[MAXN];int next[MAXL][26]
阅读全文
摘要:给出一个DNA串以及若干个带有疾病的串,问至少要改变DNA串中的几个字符,才能让它不携带病毒串。其实还是不包含若干子串的问题,可以转化为走一条不包含病毒串的路径,这条路径组成的串和给出DNA串最少相差几个字符,这样就很容易想到DP方程 d[i][j]一开始初始化为无穷大,x是j的父亲节点,也就是说存在next[x][p]=j,一般都是从前向后刷表的。flag表示这一位和DNA串中的对应字符是否相等,相等时为0,不相等时为1。 注意在走的过程中不要走到危险节点上去。#include <string.h>#include <stdio.h>#define MAXN 1001
阅读全文
摘要:给出M个单词,问长度为N的包含不少于K个单词的字符串一共有多少个。到目前为止做的自动机的题目好像都是差不多样子。。都是包含不包含单词之类的。。 这题用d[i][j][k]表示第i步走到字符j包含了单词集合k,因为一共只有10个单词,可以用二进制压缩状态表示这个集合。注意在建立trie图时要合并节点和它的fail节点的状态,一开始没想到这个WA了一次。状态转移方程为 不加优化的代码交上去时间接近TLE。。。于是改成了滚动数组,并用for循环清0,还是要跑360ms左右。这相对于1s的时限还是很慢的。。。于是点了下Statistic,发现大家都是跑了好几百ms。。。。#include ...
阅读全文
摘要:应该算是最基础的AC自动机DP了吧。。 跟前面做的两道用矩阵加速的AC自动机题目意思差不多,都是求不包含给定单词的单词数。区别就是给定单词较多,需要的字符串比较短,然后结果不取模,要用到高精度。 trie图中大约有50*10个节点,如果建立矩阵用矩阵加速无论是时间复杂度还是空间复杂度都是会超的(时间大约是500^3*log(50),空间是500^2*高精度数组)。所以这里要用到DP了,用dp[i][j]表示第i步在第j个节点的方法数。flag[k]=0表示不是非法节点,son是k的儿子节点的集合。 一开始爆空间了,POJ真心抠啊,这题就给了10M内存,后来高精度里面用char存,...
阅读全文
摘要:就是POJ2778的加强版,思路是一样的。 出现过给定的单词的单词数=总单词数-未出现过给定单词的单词数,前者等于26^1+26^2+26^3....26^l,后者等于Mat^1+Mat^2+..Mat^l,这里的Mat是根据能走的字符之间的路径数建立出来的邻接矩阵。两者求出来一减就可以了,对2^64取模可以忽略,直接用unsigned long long做,忽视溢出就等于取模了,注意减法最后结果可能小于0,要加2^64变成正数。 求a^1+..a^n用一次二分就可以,建议这种二分都写成非递归的,效率比较高而且不会有爆栈的隐患。 #include <stdio.h>#include
阅读全文
摘要:又是一道调了大半天的题,最后发现竟然是自己建立trie图的地方有个小BUG,这个小BUG在字符串匹配时没什么影响,所以一直没发现出来。刚刚学习,还是理解的不够深入啊。现在这个trie图应该算是写的很简洁了,可以拿来当模版了。 题意很简单,就是问长度为n不包含若干子串的串一共有多少个。这里可以用AC自动机DP,首先对于单词的结尾节点,标记为非法节点,一旦走到了一个非法节点,就说明包含了某个单词。网上的解题报告很多人说是AC自动机DP。但这题我的做法似乎没有用到DP,只是用矩阵加速了一下罢了。。。。首先标记出非法节点,补全trie图,用一个矩阵表示从每个合法节点到其它合法节点转移的方案,可...
阅读全文
摘要:这道题好坑啊,整整调了我一下午!! 题意很简单,就是一个裸的自动机加上解码。编码是使用8位ASCII码连接字符,然后6位一取,结尾不足6位用0补上,每6位二进制数都会对应一个字符,形成了新的字符串。解码后最好用int存每一位的内容,写起来比较方便,因为可能会有'\0'这样的字符出现。。 一开始写了个位运算版本的解码,但是一直WA。于是写了个很暴力的模拟二进制解码,竟然A了,看来自动机部分没什么问题。然后又回过头来调我的位运算版解码,怎么算都没有错啊,但是就是一直WAWAWA。。。。最后绝望之中改了下数组大小,把字符串数组从2500改成了5000。。。然后,然后就神奇的A了。。跑
阅读全文
摘要:AC自动机的模版题,应该是很多人的AC自动机处女作吧。。 在斌牛的讲解下,总算是对AC自动机了解了个大概,这里采用了AC自动机和Trie图两种写法,据说后者在AC自动机DP中用的比较多,应该很快就会做到了。。。两者差不多,Trie图利用失败指针来建图,据说AC自动机到Trie图就是转化成了确定性有穷自动机。 贴一下两种写法的代码,当模版了。 AC自动机:#include <string.h>#include <stdio.h>#include <queue>#define MAXS 500010int cas,n;char s[55],find[100000
阅读全文
摘要:感觉是不太好想的一道题,看完之后能想到是KMP,按照自己的想法敲了出来,但一直过不了。后来在网上搜题解搜到了一篇国家集训队的论文《由图论问题浅析算法优化》,论文中提到了这道题,想法很巧妙,类似于枚举,再加上KMP的优化。因为S较小(1~25),只要枚举模式串中的数字和匹配串中的数字的对应关系并且满足大小顺序关系就可以确定下来这个子串是否符合要求,具体的过程可以去看一下论文,这个算法的本质还是枚举,只是在枚举中进行了优化,并且使用KMP优化来快速找到符合对应关系的子串的位置,我的程序在POJ上跑了一秒多点,应该可以再优化。 但是上面的算法还是有缺点的,其复杂度是O(S*S*(N+M)),当...
阅读全文
摘要:简单的KMP,考察对next数组的理解,next[i]表示S[1..i]中前缀和后缀的最大匹配程度。 假设字符串长度为L,用S[i..j]表示下标从i到j的字符组成的子串。 首先next[L]=x1,说明这个字符串S[1..x1]和S[L-x1+1..L]是相同的,所以x1是一个可能的公共前缀和后缀的长度值。然后next[x1]=x2,说明S[1..x2]和S[x1-x2+1..x1]是相等的,而S[1..x1]=S[L-x1+1..L],就有S[1..x2]=S[x1-x2+1..x1]=S[L-x2+1..L],所以x2也是一个可能的前缀和后缀的公共长度值,这样一直推到next[i...
阅读全文
摘要:这题是在做KMP分类里做到的,网上搜到的题解也基本都是KMP解的。但这题根本不是KMP,在网上找了个KMP的程序,随便rand了10组数据,跑了10多秒。 题意比较纠结,就是说给你一个长度为N的串S,找出S[k..k+t-1]=s[N-t+1..N],其中1<=t<=13,k<=N-t,有多个k满足时,选择t最大的并且最靠右的,令S[N+1]=S[k+t],如果找不到符合条件的的K,则S[N+1]='0'。 正解应该是压缩状态,每个子串都由01组成,可以看作一个2进制数,因为t的范围<=13,所以用2^13就可以表示出所有的子串,用last[i][j]表
阅读全文
摘要:一道KMP的好题,可惜数据实在是太弱了。 给出R*C的字符矩阵,问至少多大面积的小矩阵可以覆盖掉整个大矩阵。很容易想到的解法就是求出循环节然后求最大公倍数,但是这样就忽视了可以不用正好覆盖这个条件,比如aaabaa可以拆成aaab/aa,aaaba/a,aaabaa,而不一定非要拆成4的倍数。正确的做法是求出每一列可能拆分的长度,然后取所有列都可以分的长度中最小的。比如 abcdeaa aaabaaa 第一个串可以分的长度有5,7,第二个串可以分的长度有4,5,6,7,所以取5作为公共长度。至于怎么求一个字符串所有可以拆分的情况,用KMP处理一遍就可以了,然后从最后一列开始,取i=...
阅读全文
摘要:考察对KMP的next数组的掌握,next数组也就是失败指针指向它失败后应该指向的位置,next[i]~i之间就是一个循环节,所以只要满足i%(i-next[i])==0即可。#include <string.h>#include <stdio.h>int cas=1,n;char s[1000005];int next[1000005];void solve(){ next[1]=0; int len=strlen(s+1); for(int i=2,j=0;i<=len;i++){ while(j>0&&s[i]!=s[j+1])j=ne
阅读全文
摘要:给出一个字符串S1和S2,可以对S1进行添加字符(insert),删除字符(delete),替换字符(replace),交换相邻字符(swap)四种操作,问是否能在dis步骤内将S1转换成S2。 用d[i][j]表示将S1[1..i]转化为S2[1..j]的最小步数。 这几种操作可以表示为:添加d[i][j]=d[i][j-1]+1,删除d[i][j]=d[i-1][j]+1,替换d[i][j]=d[i-1][j-1]+1,交换d[i][j]=d[i-2][j-2]+1(条件为s1[i-1]==s2[j]&s1[i]==s2[j-1])。 但是仅考虑到这些还是过不了这道题的,因为交换.
阅读全文
摘要:这道题目很有意思,要求简化债务关系。一开始感觉很复杂,稍加分析后发现其实就是简化这个图,让这个图每个点出度为0或者入度为0,也就是说让欠账的人直接掏钱给差钱的人就行了。所以只要读一遍数据,看一下欠钱的一共欠了多少就可以了。 1 #include<cstdio> 2 #include<string.h> 3 using namespace std; 4 int cas = 1, n, deg[1005]; 5 int main() 6 { 7 while(scanf("%d", &n), n) 8 { 9 int ans0 = 0, ans1
阅读全文
浙公网安备 33010602011771号