待填坑
A. Couple Doubi
这道题虽然看起来很变态,但是其实是一道大水题。我们来推导一下式子。首先我们要知道两个性质:
性质1:质数一定有原根。
性质2:若
g
是
对于这道题,我们任取
当
i≠p−1
时,
gi≢1
,由等比数列求和公式得到,上式
≡gpi−gigi−1≡0(mod m)
。
当
i=p−1
时,上式
≡p−1
。
这样答案就很简单了,就是
n/(p−1)
为奇数时赢,否则不赢。复杂度
O(1)
。
#include<bits/stdc++.h>
int k, p;
int main()
{
while (~scanf("%d%d", &k, &p))
if ((k / (p - 1)) & 1)
printf("YES\n");
else
printf("NO\n");
return 0;
}
B. Jump
这是一道最小费用最大流的题,建图方法如下:
每个格子在
X
部和
#include<bits/stdc++.h>
#define MAXN (300)
#define MAXM (5010)
#define MAXS (20)
const int INF = INT_MAX;
struct node{
int v, c, f, w, nxt;
}edge[MAXM];
int T, n, m, k;
int head[MAXN], edge_cnt;
int dis[MAXN];
bool vis[MAXN];
int pre[MAXN];
char str[MAXS][MAXS];
bool spfa(int s, int t, int &flow, int &cost){
std::queue <int> que;
for (int i = 0; i < MAXN; ++ i)
dis[i] = INF;
memset(vis, false, sizeof(vis));
dis[s] = 0;
vis[s] = true;
que.push(s);
while (!que.empty()){
int u = que.front();
que.pop();
vis[u] = false;
//std::cout << "f**k" << std::endl;
for (int i = head[u]; i + 1; i = edge[i].nxt){
int v = edge[i].v, c = edge[i].c, f = edge[i].f, w = edge[i].w;
if (c - f > 0 && dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
pre[v] = i;
if (!vis[v]){
vis[v] = true;
que.push(v);
}
}
}
}
//std::cout << "f**k" << std::endl;
if (dis[t] == INF)
return false;
int nowflow = INF;
for (int u = t; u != s; u = edge[pre[u] ^ 1].v)
nowflow = std::min(nowflow, edge[pre[u]].c - edge[pre[u]].f);
flow += nowflow;
cost += dis[t] * nowflow;
for (int u = t; u != s; u = edge[pre[u] ^ 1].v){
edge[pre[u]].f += nowflow;
edge[pre[u] ^ 1].f -= nowflow;
}
return true;
}
int mincost(int s, int t, int limit){
int flow = 0, cost = 0;
while (spfa(s, t, flow, cost));
if (flow < limit)
return 1;
return cost;
}
void addedge(int u, int v, int c, int w){
edge[++ edge_cnt] = {v, c, 0, w, head[u]};
head[u] = edge_cnt;
edge[++ edge_cnt] = {u, 0, 0, -w, head[v]};
head[v] = edge_cnt;
}
int main(){
scanf("%d", &T);
for (int kk = 1; kk <= T; ++ kk){
scanf("%d%d%d", &n, &m, &k);
memset(head, -1, sizeof(head));
edge_cnt = -1;
getchar();
for (int i = 0; i < n; ++ i){
for (int j = 1; j <= m; ++ j)
str[i][j] = getchar();
getchar();
}
int s = 2 * n * m + 1, t = s + 1, ext = t + 1;
for (int i = 1; i <= n * m; ++ i){
addedge(s, i, 1, 0);
addedge(i + n * m, t, 1, 0);
}
addedge(s, ext, k, 0);
for (int i = 1; i <= n * m; ++ i)
addedge(ext, i + n * m, 1, 0);
for (int i = 0; i < n; ++ i)
for (int j = 1; j <= m; ++ j){
for (int ii = i + 1; ii < n; ++ ii){
int cost = ii - i - 1;
if (str[ii][j] == str[i][j])
cost -= str[i][j] - '0';
addedge(i * m + j, n * m + ii * m + j, 1, cost);
}
for (int jj = j + 1; jj <= m; ++ jj){
int cost = jj - j - 1;
if (str[i][jj] == str[i][j])
cost -= str[i][j] - '0';
addedge(i * m + j, n * m + i * m + jj, 1, cost);
}
}
printf("Case %d : %d\n", kk, -mincost(s, t, m * n));
}
return 0;
}
E. Peter’s Hobby
这是一道概率论加
DP
的题。状态转移方程如下:设
dp[i][j]
表示第
i
天天气为
dp[i][j]=max0≤k<3dp[i−1][k]×prob2[k][j]×prob2[j][hum[i]]
。复杂度
O(50×32)
。
#include<bits/stdc++.h>
using namespace std;
#define MAXN (60)
double prob1[3][4] = {0.6, 0.2, 0.15, 0.05, 0.25, 0.3, 0.2, 0.25, 0.05, 0.10, 0.35, 0.50};
double prob2[3][3] = {0.5, 0.375, 0.125, 0.25, 0.125, 0.625, 0.25, 0.375, 0.375};
double dp[MAXN][3];
int pre[MAXN][3], a[MAXN];
vector <int> ans;
int main(){
for (int i = 0; i < 3; ++ i)
for (int j = 0; j < 4; ++ j)
prob1[i][j] = log(prob1[i][j]);
for (int i = 0; i < 3; ++ i)
for (int j = 0; j < 3; ++ j)
prob2[i][j] = log(prob2[i][j]);
int t;
scanf("%d", &t);
for (int kk = 1; kk <= t; ++ kk){
for (int i = 0; i < MAXN; ++ i)
for (int j = 0; j < 3; ++ j)
dp[i][j] = -1e20;
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++ i){
char s[10];
scanf("%s", s);
int len = strlen(s);
switch (len){
case 3: a[i] = 0; break;
case 6: a[i] = 1; break;
case 4: a[i] = 2; break;
case 5: a[i] = 3; break;
}
}
dp[0][0] = log(0.63) + prob1[0][a[0]];
dp[0][1] = log(0.17) + prob1[1][a[0]];
dp[0][2] = log(0.20) + prob1[2][a[0]];
for (int i = 1; i < n; ++ i)
for (int j = 0; j < 3; ++ j)
for (int k = 0; k < 3; ++ k)
if (dp[i - 1][j] + prob2[j][k] + prob1[k][a[i]] > dp[i][k]){
dp[i][k] = dp[i - 1][j] + prob2[j][k] + prob1[k][a[i]];
pre[i][k] = j;
}
double maxi = -1e20;
int sit;
for (int i = 0; i < 3; ++ i)
if (maxi < dp[n - 1][i]){
maxi = dp[n - 1][i];
sit = i;
}
ans.clear();
for (int i = n - 1; i >= 0; -- i){
ans.push_back(sit);
sit = pre[i][sit];
}
printf("Case #%d:\n", kk);
for (int i = ans.size() - 1; i >= 0; -- i){
switch (ans[i]){
case 0: printf("Sunny\n"); break;
case 1: printf("Cloudy\n"); break;
case 2: printf("Rainy\n"); break;
}
}
}
return 0;
}
I. Turn the Pokers
观察容易发现这样三个性质:
性质1:最后状态的0,1个数确定后,它们的任意排列都能得到。
性质2:最后状态能取到的1的个数的奇偶性完全相同。
性质3:在满足2的条件下,最后状态能取到的1的个数的最大值和最小值之间的任意数都能取到。
因此,我们用
l
和
如果
r+xi≤m
,那么
r=r+xi
;如果
l+xi≥m)
,那么
r=2m−x−l
;否则
r=m−((m⊕xi⊕r)&1
如果
l≥xi
,那么
l=l−xi
;如果
r≤xi
,那么
l=xi−r
;否则
l=(xi⊕r)&1
。
复杂度
O(n)
。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100010;
const int moder = 1e9 + 9;
int n, m, fac[MAXN], inv[MAXN], comb[MAXN];
int power(int num, int index){
int ans = 1;
while (index){
if (index & 1)
ans = 1ll * ans * num % moder;
num = 1ll * num * num % moder;
index >>= 1;
}
return ans;
}
int main(){
fac[0] = inv[0] = 1;
for (int i = 1; i < MAXN; ++ i)
fac[i] = 1ll * i * fac[i - 1] % moder;
for (int i = 1; i < MAXN; ++ i)
inv[i] = power(fac[i], moder - 2);
while (~scanf("%d%d", &n, &m)){
int low = 0, high = 0, l, h;
for (int i = 0; i <= m; ++ i)
comb[i] = 1ll * fac[m] * inv[i] % moder * inv[m - i] % moder;
for (int i = 0, x; i < n; ++ i){
scanf("%d", &x);
if (high + x <= m)
h = high + x;
else if (low + x >= m)
h = 2 * m - x - low;
else
h = m - ((m ^ x ^ high) & 1);
if (low >= x)
l = low - x;
else if (high <= x)
l = x - high;
else
l = (x ^ high) & 1;
high = h;
low = l;
}
int cnt = 0;
for (int i = low; i <= high; i += 2)
cnt = (cnt + comb[i]) % moder;
printf("%d\n", cnt);
}
return 0;
}
J. Rating
一道概率论的题。由于分数只能是50的倍数,我们把分数除以50。先考虑只有一个账号的情况:设
xi
表示当前分数为
i
,到20分所需的期望。当
方法一:用高斯消元法解上面得到的方程组。复杂度
O(n3)
。
#include<bits/stdc++.h>
# define MAXN (25)
const double eps = 1e-12;
double p, a[MAXN][MAXN];
void gauss_jordan(int n){
for (int i = 0; i < n; ++ i){
int r = i;
for (int j = i + 1; j < n; ++ j){
if (fabs(a[j][i]) - fabs(a[r][i]) > eps){
r = j;
}
}
if (r != i){
for (int j = i; j <= n; ++ j){
std::swap(a[i][j], a[r][j]);
}
}
for (int k = 0; k < n; ++ k){
if (k == i){
continue;
}
for (int j = n; j >= i; -- j){
a[k][j] -= a[k][i] / a[i][i] * a[i][j];
}
}
}
}
int main(){
while (~scanf("%lf", &p)){
for (int i = 2; i <= 19; ++ i){
a[i][i] = 1;
a[i][i + 1] = -p;
a[i][i - 2] = p - 1;
a[i][20] = 1;
}
a[0][0] = p;
a[0][1] = -p;
a[0][20] = 1;
a[1][1] = 1;
a[1][2] = -p;
a[1][0] = p - 1;
a[1][20] = 1;
gauss_jordan(20);
printf("%lf\n", 2 * a[0][20] / a[0][0] - a[19][20] / a[19][19]);
}
return 0;
}
方法二:我们令
ti=x0−xi
,那么
ti
就表示从0分到
i
分的期望。显然有
#include<bits/stdc++.h>
#define MAXN (25)
const double eps = 1e-12;
double p, a[MAXN];
int main(){
while(~scanf("%lf", &p)){
a[0] = 0;
a[1] = 1 / p;
a[2] = 1 / p + 1 / p / p;
for (int i = 3; i <= 20; ++ i){
a[i] = 1 / p + a[i - 1] / p - (1 - p) * a[i - 3] / p;
}
printf("%lf\n", a[19] + a[20]);
}
return 0;
}