T1:求一个有多少个不同的长度为n的序列满足|ai-i|<=k,n<=1e9,k<=4。
题解:一开始打了个表,发现k=1的时候是个斐波那契,再看其他情况,感觉很有规律的样子,于是就开始了2个小时找规律的不归路。
其实题目已经很明显了,n为1e9,不是找规律的O(1)就是矩阵快速幂的三方log。这题看到k这么小,第一反应就应该是状压DP。考虑用一个2*k+1位的二进制数来表示左右的占用情况,然后转移。
不过这样矩阵大小是256的,这还跑不过去。我们再看,发现,状态一定是有k个位置为1(被占)的,那么就是C(9,4)=126的状态数,这样就很好了,可以直接预处理出转移给每个状态标号然后矩阵快速幂DP了。
为什么状态一定要有k个1呢?我们这么考虑:以k=2为例,我们对于起始状态以及终止状态设定为00011,这样能使得不会出现第一个数以前以及最后一个数之后被占的情况。然后我们右移一位就能转移到下一个数去。转移的时候我们发现,如果被拿掉的这一位是0(没有被占),这是不可以的,因为除了它没有其他数字可以去占了,它必须拿掉这个0。也就是说,有0的时候我们是直接右移,不添加任何一个1。如果末尾不是0,那么一个1被吃掉,前面又要占掉一个位置(增加1个1),1的个数不变。所以始终是k个1。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define N 150 5 #define mod 1000000007 6 7 inline LL read(){ 8 LL x=0,f=1; char a=getchar(); 9 while(a<'0' || a>'9') {if(a=='-') f=-1; a=getchar();} 10 while(a>='0' && a<='9') x=x*10+a-'0',a=getchar(); 11 return x*f; 12 } 13 14 int n,K,cnt,id[2000],mark[N]; 15 16 struct matrix{ 17 int s[N][N]; 18 inline void reset(){memset(s,0,sizeof(s));} 19 }w,dp; 20 21 matrix operator * (matrix a,matrix b){ 22 matrix ret; ret.reset(); 23 for(int i=1;i<=cnt;i++) 24 for(int j=1;j<=cnt;j++) 25 for(int k=1;k<=cnt;k++) 26 ret.s[i][j]=((LL)ret.s[i][j]+1LL*a.s[i][k]*b.s[k][j])%mod; 27 return ret; 28 } 29 30 void dfs(int x,int now,int num){ 31 if(x>2*K+1) { 32 if(num==K) id[now]=++cnt,mark[cnt]=now; 33 return; 34 } 35 dfs(x+1,(now<<1)|1,num+1); 36 dfs(x+1,now<<1,num); 37 } 38 39 inline void prework(){ 40 cnt=0; memset(id,0,sizeof(id)); memset(mark,0,sizeof(mark)); 41 dfs(1,0,0); 42 w.reset(); 43 for(int i=1;i<=cnt;i++){ 44 int x=mark[i]; 45 if(x&1){ 46 for(int k=0;k<2*K+1;k++) if(((1<<k)&x)==0) w.s[i][id[(x|(1<<k))>>1]]=1; 47 } 48 else w.s[i][id[x>>1]]=1; 49 } 50 dp.reset(); 51 dp.s[1][id[(1<<K)-1]]=1; 52 } 53 54 inline void fpow(int k){ 55 while(k){ 56 if(k&1) dp=dp*w; 57 k>>=1; w=w*w; 58 } 59 } 60 61 int main(){ 62 while(scanf("%d%d",&n,&K)!=EOF){ 63 if(!K) {puts("1");continue;} 64 prework(); fpow(n); 65 printf("%d ",dp.s[1][id[(1<<K)-1]]); 66 } 67 return 0; 68 }
T2:给定一棵树,每个节点的字符为它的度数。求解有多少个本质不同的子串满足结尾是开头的祖先(包括自己)。
题解:其实就是众神眷顾的幻想乡,只不过字符集有点问题而已。
以下这个代码如果不被卡还是跑得挺快的,当然极端数据可以卡到3000ms。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define N 500005 5 6 inline LL read(){ 7 LL x=0,f=1; char a=getchar(); 8 while(a<'0' || a>'9') {if(a=='-') f=-1; a=getchar();} 9 while(a>='0' && a<='9') x=x*10+a-'0',a=getchar(); 10 return x*f; 11 } 12 13 namespace Suffix_Automation{ 14 int sroot=1,cnt=1; 15 struct edges{ 16 int val,to; 17 bool operator < (const edges& w)const{ 18 return val<w.val; 19 } 20 }; 21 struct sam{ 22 int par,len; 23 set<edges>s; 24 }a[2*N]; 25 inline int add(int last,int v){ 26 edges tr=(edges){v,0}; 27 if(a[last].s.count(tr)) return a[last].s.find(tr)->to; 28 int p=last,now=++cnt; a[now].len=a[p].len+1; 29 tr.to=now; 30 while(p && !a[p].s.count(tr)) spj++,a[p].s.insert(tr),p=a[p].par; 31 if(!p) a[now].par=sroot; 32 else{ 33 int q=a[p].s.find(tr)->to; 34 if(a[q].len==a[p].len+1) a[now].par=q; 35 else{ 36 int nq=++cnt; 37 a[nq]=a[q]; a[nq].len=a[p].len+1; 38 a[q].par=a[now].par=nq; 39 tr.to=nq; 40 while(p && a[p].s.count(tr) && a[p].s.find(tr)->to==q) 41 a[p].s.erase(a[p].s.find(tr)),a[p].s.insert(tr),p=a[p].par; 42 } 43 } 44 return now; 45 } 46 } 47 48 #define SAM Suffix_Automation 49 50 int n,head[N],cnt=1,d[N],final[N]; 51 52 struct edges{ 53 int to,next; 54 }e[2*N]; 55 56 inline void insert(){ 57 int u=read(),v=read(); d[u]++; d[v]++; 58 e[++cnt]=(edges){v,head[u]};head[u]=cnt; 59 e[++cnt]=(edges){u,head[v]};head[v]=cnt; 60 } 61 62 void dfs(int x,int fa){ 63 final[x]=SAM::add(final[x],d[x]); 64 if(!final[x]) return 0; 65 for(int i=head[x];i;i=e[i].next){ 66 if(e[i].to==fa) continue; 67 final[e[i].to]=final[x]; 68 dfs(e[i].to,x); 69 } 70 } 71 72 int main(){ 73 n=read(); 74 for(int i=1;i<n;i++) insert(); 75 final[1]=1; dfs(1,0); 76 LL ans=0; 77 for(int i=1;i<=SAM::cnt;i++) ans+=SAM::a[i].len-SAM::a[SAM::a[i].par].len; 78 printf("%lld ",ans); 79 return 0; 80 }
T3:分块+bitset,不多说了(其实是没写代码。。