A. Hard to prepare (dp)
题意
NN个客人,主人手上有个面具。
现在,NN个人围着圆桌相邻而坐,主人会给他们每个人发一个面具,相邻两个人得到的面具必须满足条件:ii jj为正数。
问:有多少种方案?最终答案对 取膜
分析
mask[i] XNOR mask[1] >> 0 即:2进制下至少一位相同
定义:
- dp[i][0]dp[i][0] —— 至第ii个人分发面具的方案数,且 mask[i] = mask[1]
- —— 至第ii个人分发面具的方案数,且 mask[i] XNOR mask[1] = 0
- —— 至第ii个人分发面具的方案数,且 mask[i] mask[1],mask[i] XNOR mask[1] ≠≠ 0
可以推得转移方程:
- dp[i][0]=dp[i−1][0]+dp[i−1][2]dp[i][0]=dp[i−1][0]+dp[i−1][2]
- dp[i][1]=dp[i−1][1]+dp[i−1][2]dp[i][1]=dp[i−1][1]+dp[i−1][2]
- dp[i][0]=(dp[i−1][0]+dp[i−1][1])∗(2k−2)+dp[i−1][2]∗(2k−3)dp[i][0]=(dp[i−1][0]+dp[i−1][1])∗(2k−2)+dp[i−1][2]∗(2k−3)
代码
#include<bits/stdc++.h>
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;
}
B. BE GE or NE (记忆化搜索)
题意
Boy 和 Girl 玩一场游戏,轮流行动,一共行动N次,初始分数为m,每一回合对于当前的分数可以做三种操作,+a[i],-b[i],*(-1)。如果a[i],b[i],c[i]某一个数等于0,那么不能选择这个操作。
过程中,Boy 希望分数尽量 ≥k≥k,Girl希望分数尽量 ≤l≤l,两人都按最优方案抉择,输出最终结果。
分析
对于当前分数scsc,如果是Bog行动,那么取最大分数,如果是Girl行动,那么取最小分数。
记忆化搜索所有情况,时间复杂度O(200∗n)O(200∗n)
代码
#include<bits/stdc++.h>
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;
}
I. Characters with Hash (模拟)
题意
将一个字符串ss和一个字符,按所给的hash方法进行转换,hash方法为|L−s[i]||L−s[i]|,保留前导0,显然每个s[i]s[i]对应一个两位数。得到整个字符串的hash值后,删除前导0
分析
按题意模拟
代码
#include<bits/stdc++.h>
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;i<n;i++)
{
int 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;
}
K. Morgana Net (矩阵快速幂)
题意
Ak+1[i][j]=f(∑i+mp=i−m∑i+mq=i−mAk[p][q]∗B[p−i+m+1][q−j+m+1])Ak+1[i][j]=f(∑p=i−mi+m∑q=i−mi+mAk[p][q]∗B[p−i+m+1][q−j+m+1])
给出A0A0,求AtAt中有多少个1
分析
对于Ak[i][j]Ak[i][j],由∑i+mp=i−m∑i+mq=i−mAk−1[p][q]∑p=i−mi+m∑q=i−mi+mAk−1[p][q]转移而来,可以发现同一位置是通过同一个m∗mm∗m矩阵转移的,所以该矩阵的每一个数的贡献值是固定的,贡献值为B[p−i+m+1][q−j+m+1])B[p−i+m+1][q−j+m+1])
创建矩阵:
- 1∗n21∗n2 的答案矩阵S=[A1,1,A1,2,...,An,n]S=[A1,1,A1,2,...,An,n]
- n2∗n2n2∗n2的转移矩阵BB,且,即Ak−1[p][q]Ak−1[p][q]对Ak[i][j]Ak[i][j]存在贡献,贡献值为b[p−i+m+1][q−j+m+1]b[p−i+m+1][q−j+m+1]
题目数组下标由(1,1)开始,代码数组由(0,0)开始
代码
#include<bits/stdc++.h>
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;i<a.n;i++)
for (int j=0;j<b.m;j++)
{
long long tmp = 0;
for (int k=0;k<a.m;k++)
tmp += (a.d[i][k]*b.d[k][j] & 1);
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;i<n;i++)
for (int j=0;j<n;j++)
{
A.d[0][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;i<n*n;i++) ans += A.d[0][i];
printf("%d\n",ans);
return;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d %d %d",&n,&m,&t);
for (int i=0;i<n;i++)
for (int j=0;j<n;j++) scanf("%d",&a[i][j]);
for (int i=0;i<m;i++)
for (int j=0;j<m;j++) scanf("%d",&b[i][j]);
solve();
}
return 0;
}