A HDU 5948
B HDU 5949
送温暖的签到题,就不说了
C HDU 5950 Recursive sequence
求一个递推式:
f
n
=
2
f
n
−
2
+
f
n
−
1
+
i
4
f_n=2f_{n-2}+f_{n-1}+i^4
fn=2fn−2+fn−1+i4 ,并且给出了初始的两项
首先数列本身是没有规律的,所以我们考虑矩阵快速幂.
如果只有递推式前两项的话,比较简单。但是
i
4
i^4
i4如何解决?
我们考虑将他加到我们要构造的矩阵中,发现只有一个四次项不够完成转移,但是
(
a
+
1
)
4
=
a
4
+
4
a
3
+
6
a
2
+
4
a
+
1
(a+1)^4 = a^4 + 4 a^3 + 6 a^2 + 4 a + 1
(a+1)4=a4+4a3+6a2+4a+1,于是我们就把3次项,2次项,1次项,常数项都添加进去……
这样就得到了我们想要的7x7的矩阵 ,剩下的交给矩阵快速幂就可以了
注意递推开始的第一项是3,要把3的各个幂次填进去。
取模的值是超过int的,要注意开long long
#include<bits/stdc++.h>
#include<cstring>
using namespace std;
#define ll long long
const ll mod = 2147493647;
struct mat
{
ll m[7][7];
void show()
{
for(int i = 0; i<7; i++)
{
for(int j = 0; j<7; j++)
{
printf("%d ",m[i][j]);
}
printf("\n");
}
printf("\n");
}
};
mat mul(mat a,mat b)
{
mat res;
memset(res.m,0,sizeof(res.m));
for(int i =0; i<7; i++)
{
for(int k = 0; k<7; k++)
{
for(int j = 0; j<7; j++)
{
res.m[i][j] += ((a.m[i][k]*b.m[k][j])%mod);
res.m[i][j]%=mod;
}
}
}
return res;
}
mat pow_mat(mat a,ll b)
{
mat t;
memset(t.m,0,sizeof(t.m));
for(int i = 0; i<7; i++)
t.m[i][i] = 1;
while(b)
{
if(b & 1)
t = mul(t,a);
a = mul(a,a);
b >>=1;
}
return t;
}
ll init[7][7] =
{
{0,2,0,0,0,0,0},
{1,1,0,0,0,0,0},
{0,1,1,0,0,0,0},
{0,0,4,1,0,0,0},
{0,0,6,3,1,0,0},
{0,0,4,3,2,1,0},
{0,0,1,1,1,1,1}
};
int main()
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
ll n,a,b;
scanf("%I64d%I64d%I64d",&n,&a,&b);
if(n <= 1)
printf("%I64d\n",a);
else if(n == 2)
printf("%I64d\n",b);
else
{
mat A;
for(int i = 0; i<7; i++)
{
for(int j = 0; j<7; j++)
{
A.m[i][j] = init[i][j];
}
}
mat B;
memset(B.m,0,sizeof(B.m));
B.m[0][0] = a;
B.m[0][1] = b;
B.m[0][2] = 81;
B.m[0][3] = 27;
B.m[0][4] = 9;
B.m[0][5] = 3;
B.m[0][6] = 1;
//B.show();
mat res = mul(B,pow_mat(A,n-2));
//mul(A,B).show();
//ll res = B.m[0][1]*a+B.m[1][1]*b+B.m[2][1]*(81);
//res.show();
printf("%I64d\n",res.m[0][1]);
}
}
return 0;
}
HDU 5952 Counting Cliques
给出一个n个点,m条边的完全图,求出有多少个大小为s的团。
搜团我们可以存一个candidate列表,然后从一个点向前扩展的时候,我们就将但前的点于candidate中的节点进行对比,如果当前节点都造列表中,就将这个点添加进去,然后从这个点搜下去。
红宝书上面有提过这个东西,对于我来说是第一次接触。算是学到了。
这里还有一个小技巧,我们连边的时候可以按照标号从小到大建成单向的,不影响团的性质,就不用考虑重复的问题了。
#include <bits/stdc++.h>
#define ll long long
#define pr pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define next fuck
using namespace std;
const int MAXN =105;
vector<int> edge[MAXN];
int temp[MAXN];
bool g[MAXN][MAXN],in[MAXN];
int n,m,s;
int tot,ans;
void dfs(int u,int sz)
{
if(sz == s)
{
ans++;
return ;
}
for(int v : edge[u])
{
int j;
for(j = 0;j<tot;j++)
{
if(!g[temp[j]][v]) break;
}
if(j == tot)
{
temp[tot++] = v;
dfs(v,sz+1);
tot--;
}
}
}
int main()
{
int ca;
cin>>ca;
while(ca--)
{
cin>>n>>m>>s;
for(int i = 1;i<=n;i++) edge[i].clear();
memset(g,0,sizeof(g));
for(int i = 0;i<m;i++)
{
int u,v;
cin>>u>>v;
if(u > v) swap(u,v);
g[u][v] = 1;
edge[u].pb(v);
}
ans = 0;
n = n -s+1;
for(int i = 1;i<=n;i++)
{
tot = 0;
temp[tot++] =i;
dfs(i,1);
}
printf("%d\n",ans);
}
return 0;
}