N N 个客人,主人手上有 2k 2 k 个面具。
现在, N N 个人围着圆桌相邻而坐,主人会给他们每个人发一个面具,相邻两个人得到的面具 i,j i , j 必须满足条件: i i XNOR X N O R j j 为正数。
问:有多少种方案?最终答案对 109+7 10 9 + 7 取膜
mask[i] XNOR mask[1] > > 0 即:2进制下至少一位相同
定义:
可以推得转移方程:
#include
using namespace std;
const int maxn = 1e6+100;
const int mod = 1e9+7;
int T,n,k;
long long dp[maxn][3],p[maxn];
void pre()
{
p[0] = 1;
for (int i=1;i<=maxn-100;i++)
{
p[i] = p[i-1]+p[i-1];
if (p[i] >= mod) p[i]-=mod;
}
return;
}
int main()
{
pre();
scanf("%d",&T);
while (T--)
{
scanf("%d %d",&n,&k);
dp[1][0] = 1;
for (int i=2;i<=n;i++)
{
dp[i][0] = (dp[i-1][0] + dp[i-1][2]) % mod;
dp[i][1] = (dp[i-1][1] + dp[i-1][2]) % mod;
dp[i][2] = (((dp[i-1][0] + dp[i-1][1])*(p[k]-2+mod)) % mod + (dp[i-1][2]*(p[k]-3+mod)) % mod) % mod;
}
long long ans = ((dp[n][0] + dp[n][2])*p[k]) % mod;
printf("%lld\n",ans);
}
return 0;
}
Boy 和 Girl 玩一场游戏,轮流行动,一共行动N次,初始分数为m,每一回合对于当前的分数可以做三种操作,+a[i],-b[i],*(-1)。如果a[i],b[i],c[i]某一个数等于0,那么不能选择这个操作。
过程中,Boy 希望分数尽量 ≥k ≥ k ,Girl希望分数尽量 ≤l ≤ l ,两人都按最优方案抉择,输出最终结果。
对于当前分数 sc s c ,如果是Bog行动,那么取最大分数,如果是Girl行动,那么取最小分数。
记忆化搜索所有情况,时间复杂度 O(200∗n) O ( 200 ∗ n )
#include
using namespace std;
const int maxn = 1010;
const int INF = 1e8;
int n,m,l,r,a[maxn],b[maxn],c[maxn];
int dp[maxn][210];
int dfs(int i,int sc)
{
if (i == n+1) return sc;
if (dp[i][sc+100] != INF) return dp[i][sc+100];
int cnt,tmp;
if (i % 2)/**first**/
{
tmp = -100;
if (a[i])
{
cnt = min(100,sc+a[i]);
tmp = max(tmp,dfs(i+1,cnt));
}
if (b[i])
{
cnt = max(-100,sc-b[i]);
tmp = max(tmp,dfs(i+1,cnt));
}
if (c[i])
{
cnt = sc*(-1);
tmp = max(tmp,dfs(i+1,cnt));
}
tmp = min(100,max(-100,tmp));
dp[i][sc+100] = tmp;
}
else
{
tmp = 100;
if (a[i])
{
cnt = min(100,sc+a[i]);
tmp = min(tmp,dfs(i+1,cnt));
}
if (b[i])
{
cnt = max(-100,sc-b[i]);
tmp = min(tmp,dfs(i+1,cnt));
}
if (c[i])
{
cnt = sc*(-1);
tmp = min(tmp,dfs(i+1,cnt));
}
dp[i][sc+100] = tmp;
}
return dp[i][sc+100];
}
int main()
{
scanf("%d %d %d %d",&n,&m,&r,&l);
for (int i=1;i<=n;i++)
scanf("%d %d %d",&a[i],&b[i],&c[i]);
for (int i=0;i<=n;i++)
for (int j=0;j<=200;j++) dp[i][j] = INF;
int ans = dfs(1,m);
if (ans >= r) printf("Good Ending\n");
else
if (ans <= l) printf("Bad Ending\n");
else
printf("Normal Ending\n");
return 0;
}
将一个字符串 s s 和一个字符 L L ,按所给的hash方法进行转换,hash方法为 |L−s[i]| | L − s [ i ] | ,保留前导0,显然每个 s[i] s [ i ] 对应一个两位数。得到整个字符串的hash值后,删除前导0
按题意模拟
#include
using namespace std;
const int maxn = 2e6+100;
int T,n,a[maxn];
char c,s[maxn];
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d %c",&n,&c);
scanf("%s",s);
for (int i=0;iint cnt = abs(s[i] - c);
a[i*2] = cnt/10;
a[i*2+1] = cnt%10;
}
int k = 2*n-1;
for (int i=0;i<2*n;i++)
if (a[i] != 0)
{
k = i;
break;
}
printf("%d\n",n*2-k);
}
return 0;
}
Ak+1[i][j]=f(∑i+mp=i−m∑i+mq=i−mAk[p][q]∗B[p−i+m+1][q−j+m+1]) A k + 1 [ i ] [ j ] = f ( ∑ p = i − m i + m ∑ q = i − m i + m A k [ p ] [ q ] ∗ B [ p − i + m + 1 ] [ q − j + m + 1 ] )
给出 A0 A 0 ,求 At A t 中有多少个1
对于 Ak[i][j] A k [ i ] [ j ] ,由 ∑i+mp=i−m∑i+mq=i−mAk−1[p][q] ∑ p = i − m i + m ∑ q = i − m i + m A k − 1 [ p ] [ q ] 转移而来,可以发现同一位置是通过同一个 m∗m m ∗ m 矩阵转移的,所以该矩阵的每一个数的贡献值是固定的,贡献值为 B[p−i+m+1][q−j+m+1]) B [ p − i + m + 1 ] [ q − j + m + 1 ] )
创建矩阵:
题目数组下标由(1,1)开始,代码数组由(0,0)开始
#include
using namespace std;
int T,n,m,t;
int a[20][20],b[20][20];
struct Matrix
{
int n,m,d[70][70];
Matrix (int N=0, int M=0)
{
n = N, m = M;
memset(d,0,sizeof(d));
}
friend Matrix operator * (const Matrix &a, const Matrix &b)
{
Matrix c(a.n,b.m);
for (int i=0;ifor (int j=0;jlong long tmp = 0;
for (int k=0;k1);
c.d[i][j] = tmp & 1;
}
return c;
}
};
void solve()
{
Matrix A(1,n*n),B(n*n,n*n);
m = m/2;
for (int i=0;ifor (int j=0;j0][i*n+j] = a[i][j] & 1;
for (int p = i-m; p <= i+m; p++)
for (int q = j-m; q <= j+m; q++)
{
if (p < 0 || q < 0 || p >= n || q >= n) continue;
B.d[p*n+q][i*n+j] = b[p-i+m][q-j+m] & 1;
}
}
while (t)
{
if (t & 1) A = A*B;
B = B*B;
t = t>>1;
}
int ans = 0;
for (int i=0;i0][i];
printf("%d\n",ans);
return;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d %d %d",&n,&m,&t);
for (int i=0;ifor (int j=0;jscanf("%d",&a[i][j]);
for (int i=0;ifor (int j=0;jscanf("%d",&b[i][j]);
solve();
}
return 0;
}