3.17日常小测

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,不多说了(其实是没写代码。。

原文地址:https://www.cnblogs.com/enigma-aw/p/6568030.html