【10.4校内测试】【轮廓线DP】【中国剩余定理】【Trie树+博弈】
考场上几乎是一看就看出来轮廓线叻...可是调了两个小时打死也过不了手出样例!std发下来一对,特判对的啊,转移对的啊,$dp$数组竟然没有取max!!!
某位考生当场死亡。
结果下午又请了诸位dalao来看为什么剩下wa两个点!结果数组开小。
某位考生再次死亡。
#include<bits/stdc++.h>
#define RG register
using namespace std; int dp[][(<<)+], cnt[(<<)+];
int R, C, a[][];////////不开够影响很大!! int count(int sta) {
int num = ;
while(sta) {
if(sta & ) num ++;
sta >>= ;
}
return num;
} void init() {
scanf("\n");
for(int i = ; i <= R; i ++) {
char s; int cnt = ;
s = getchar();
while(s != '\n') {
a[i][++a[i][]] = s - 'A' + ;
s = getchar();
}
}
for(int i = ; i < ( << C); i ++)
cnt[i] = count(i);
} struct Node {
int n1, n2;
Node(int n1 = , int n2 = ) :
n1(n1), n2(n2) { }
}; inline Node check(int sta, int pos, int line) {
int s1 = sta >> (pos - ), s2 = sta & (( << (pos - )) - );
int num1 = cnt[s1], num2 = cnt[s2];
if(num1 > a[line-][] || num2 > a[line][] || num2 + C - pos + < a[line][] || num1 + pos - < a[line-][]) return Node(-, -);
return Node(num1, num2);
} int main() {
freopen("group.in", "r", stdin);
freopen("group.out", "w", stdout);
scanf("%d%d", &R, &C);
init();
int now = ;
for(RG int i = ; i <= R; i ++) {
for(RG int j = ; j <= C; j ++) {
now ^= ;
memset(dp[now], , sizeof(dp[now]));
for(RG int s = ; s < ( << C); s ++) {
int pre = s & ( << (C - )); int las = s & ;
Node opt = check(s, j, i);
if(opt.n1 == -) continue;
int q = opt.n1, p = opt.n2;
int num1, num2;
if(!pre) num1 = ;
else num1 = a[i-][a[i-][]-q+];
if(!las || j == ) num2 = ;
else num2 = a[i][p];
if(p + <= a[i][]) {
int ss = (s ^ pre) << | ;
int t = dp[now ^ ][s];
if(a[i][p+] == num1 && pre) t += ;
if(a[i][p+] == num2 && las) t += ;
dp[now][ss] = max(t, dp[now][ss]);////我暴毙
}
int ss = (s ^ pre) << ;
dp[now][ss] = max(dp[now][ss], dp[now ^ ][s]);
}
}
}
int ans = ;
for(int s = ; s < ( << C); s ++) {
if(cnt[s] != a[R][]) continue;
ans = max(ans, dp[now][s]);
}
printf("%d", ans);
return ;
}
我们可以发现在模数为质数时,可以直接用组合+求逆元计算出来,但是求逆元只能是在模数与要求逆元数互质时才行。
又因为题目明显暗示$MOD$由质数组成,所以直接套中国剩余定理即可。需要注意的是,对于每个分解出来的质因子都要重新求对应的逆元和阶乘。
#include<bits/stdc++.h>
#define LL long long
using namespace std; int n, m, T, MOD;
LL fac[], va[], vm[], inv[];
LL isnot[], prime[], t; void div() {
isnot[] = ;
for(int i = ; i <= ; i ++) {
if(!isnot[i])
prime[++t] = i;
for(int j = ; j <= t; j ++) {
int to = prime[j] * i;
if(to > ) break;
isnot[to] = ;
if(i % prime[j] == ) break;
}
}
} LL tot;
void init() {
div();
LL tmp = MOD;
for(LL i = ; i * i <= tmp && tmp != ; i ++)
if(tmp % i == ) vm[++tot] = i, tmp /= i;
if(tmp > ) vm[++tot] = tmp;
} LL mpow(LL a, LL b, LL mod) {
LL ans = ;
for(; b; b >>= , a = a * a % mod)
if(b & ) ans = ans * a % mod;
return ans;
} LL rev(LL a, LL mod) {
return mpow(a, mod - , mod);
} LL C(LL q, LL p, LL mod) {
if(p > q) return ;
return fac[q] * inv[p] % mod * inv[q-p] % mod;
} LL Lucas(LL x, LL y, LL mod) {
if(x < y) return ;
if(y == ) return ;
return Lucas(x / mod, y / mod, mod) * C(x % mod, y % mod, mod) % mod;
} LL Chinese_remainder_theorem() {
LL ans = ;
for(LL i = ; i <= tot; i ++) {
LL mi = MOD / vm[i];
LL rei = rev(mi, vm[i]);
ans = (ans + mi * rei % MOD * va[i] % MOD) % MOD;
}
return ans;
} int main() {
freopen("visit.in", "r", stdin);
freopen("visit.out", "w", stdout);
scanf("%d%d", &T, &MOD);
scanf("%d%d", &n, &m);
if(n < ) n = -n;
if(m < ) m = -m;
int c = (T - n - m) / ;
if(T < n + m || (n + m - T) % == ) {
printf("0\n"); return ;
}
init();
for(LL k = ; k <= tot; k ++) {
fac[] = ;
for(long long i = ;i <= T;i ++)
fac[i] = 1ll * fac[i-] * i % vm[k];
inv[] = inv[] = ;
for(long long i = ;i <= T;i ++)
inv[i] = 1ll * inv[vm[k] % i] * (vm[k] - vm[k] / i) % vm[k];
for(long long i = ;i <= T;i ++)
inv[i] = 1ll * inv[i] * inv[i - ] % MOD;
for(LL i = ; i <= c; i ++) {
LL j = c - i;
va[k] = (va[k] + 1ll * Lucas(T, i, vm[k]) * Lucas(T-i, j, vm[k]) % vm[k] * 1ll * Lucas(T-i-j, i+n, vm[k]) % MOD) % MOD;
}
}
LL ans = Chinese_remainder_theorem();
printf("%lld", ans);
return ;
}
由字符串的前缀和想到建$Trie$树。我们发现,对于$Trie$树上某一节点,如果它的儿子有一个是可以选择必胜,那么当前节点就可以选择必败;如果它的儿子有一个是可以选择必败,那么当前节点就可以选择必胜;如果它的儿子全都可胜可败,那么它就没有选择权利;如果它的儿子有一个没有选择权利,那么它就可胜可败。在$Trie$树上直接深搜处理出每个节点的状态。
出来后如果根节点可胜可败,那么$Pure$就可以选择前面所有局都输,最后胜,因此她必胜;如果根节点必败,那么$Dirty$必胜;如果根节点必胜,那么要看局数$k$的奇偶性;如果根节点无法选择,也是$Dirty$必胜。
#include<bits/stdc++.h>
using namespace std; int n, k;
int son[][], tail;
char str[]; void add(char *s) {
int nd = ; int len = strlen(s);
for(int i = ; i < len; i ++) {
int t = s[i] - 'a';
if(!son[nd][t]) son[nd][t] = ++ tail;
nd = son[nd][t];
}
} int dp[];
int Dfs(int u) {
int fl = -, sum = , num = ;
for(int i = ; i < ; i ++) {
if(son[u][i]) {
sum ++;
fl = Dfs(son[u][i]);
if(fl == ) dp[u] |= ;
if(fl == ) dp[u] |= ;
if(fl == ) dp[u] |= ;
if(fl == ) num ++;
}
}
if(num == sum) dp[u] = ;
if(fl == -) dp[u] = ;
return dp[u];
} int main() {
freopen("strGame.in", "r", stdin);
freopen("strGame.out", "w", stdout);
int T;
scanf("%d", &T);
while(T --) {
memset(son, , sizeof(son));
memset(dp, , sizeof(dp));
tail = ;
scanf("%d%d", &n, &k);
for(int i = ; i <= n; i ++) {
scanf("%s", str);
add(str);
}
Dfs();
if(dp[] == ) printf("Pure\n");
else if(dp[] == ) {
printf("Dirty\n");
} else if(dp[] == ) {
if(k % ) printf("Pure\n");
else printf("Dirty\n");
} else printf("Dirty\n");
}
return ;
}
【10.4校内测试】【轮廓线DP】【中国剩余定理】【Trie树+博弈】的更多相关文章
- 【10.29校内测试】【线段树】【DP】【二进制Trie树求最小值最大】
Solution 标程太暴力惹QAQ 相当于是26棵线段树的说QAQ 不过我写了另一种写法,从大到小枚举每一个字母,标记字典序在这个字母之上的位置为1,每次都建一棵线段树,维护1的数量,即区间和. 修 ...
- 【10.17校内测试】【二进制数位DP】【博弈论/预处理】【玄学(?)DP】
Solution 几乎是秒想到的水题叻! 异或很容易想到每一位单独做贡献,所以我们需要统计的是区间内每一位上做的贡献,就是统计区间内每一位是1的数的数量. 所以就写数位dp辣!(昨天才做了数字统计不要 ...
- 【10.31校内测试】【组合数学】【记忆化搜索/DP】【多起点多终点二进制拆位Spfa】
Solution 注意取模!!! Code #include<bits/stdc++.h> #define mod 1000000007 #define LL long long usin ...
- 【10.26校内测试】【状压?DP】【最小生成树?搜索?】
Solution 据说正解DP30行??? 然后写了100行的状压DP?? 疯狂特判,一算极限时间复杂度过不了aaa!! 然而还是过了....QAQ 所以我定的状态是待转移的位置的前三位,用6位二进制 ...
- 【10.5校内测试】【DP】【概率】
转移都很明显的一道DP题.按照不优化的思路,定义状态$dp[i][j][0/1]$表示吃到第$i$天,当前胃容量为$j$,前一天吃(1)或不吃(0)时能够得到的最大价值. 因为有一个两天不吃可以复原容 ...
- 【10.3校内测试【国庆七天乐!】】【DP+组合数学/容斥】【spfa多起点多终点+二进制分类】
最开始想的暴力DP是把天数作为一个维度所以怎么都没有办法优化,矩阵快速幂也是$O(n^3)$会爆炸. 但是没有想到另一个转移方程:定义$f[i][j]$表示每天都有值的$i$天,共消费出总值$j$的方 ...
- 【10.11校内测试】【优先队列(反悔贪心)】【莫队】【stl的应用??离线处理+二分】
上次做过类似的题,原来这道还要简单些?? 上次那道题是每天可以同时买进卖出,所以用两个优先队列,一个存买进,一个存卖出(供反悔的队列). 这道题实际上用一个就够了???但是不好理解!! 所以我还是用了 ...
- 【10.7校内测试】【队列滑窗】【2-sat】【贪心+栈二分+线段树(noip模拟好题)】【生日祭!】
比较好想的一道题,直接用队列滑窗,因为扫一遍往队列里加东西时,改变的只有一个值,开桶储存好就行了! #include<bits/stdc++.h> using namespace std; ...
- 【10.6校内测试】【小模拟】【hash+线段树维护覆盖序列】
一开始看到题就果断跳到T2了!!没想到T2才是个大坑,浪费了两个小时QAQ!! 就是一道小模拟,它怎么说就怎么走就好了! 为什么要用这么多感叹号!!因为统计答案要边走边统计!!如果每个数据都扫一遍20 ...
随机推荐
- Linux时间子系统之七:定时器的应用--msleep(),hrtimer_nanosleep()【转】
转自:http://blog.csdn.net/droidphone/article/details/8104433 我们已经在前面几章介绍了低分辨率定时器和高精度定时器的实现原理,内核为了方便其它子 ...
- nanosleep()
函数原型 #include <time.h> int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); 描述 ...
- win7 64位mysql安装及navicat 解压版
教程:http://jingyan.baidu.com/article/f3ad7d0ffc061a09c3345bf0.html Mysql修改设置root密码的命令及方法:http://jingy ...
- linux终端操作快捷键
终端操作快捷键: 新建家目录下终端窗口:Ctrl+Alt+t在当期当前路径下新建终端窗口:Ctrl+Shift+n退出终端窗口:Ctrl+Shift+q 多个终端窗口之间相互切换:Tab+Alt 终端 ...
- jQuery中绑定事件的几种方法
以click事件为例,jQuery中绑定事件有三种方法: (1)target.click(function(){}); (2)target.bind("click",functi ...
- C++之构造函数的继承
#include<iostream> usingnamespace std; classBase1 { public: Base1()=default; Base1(const strin ...
- Effective STL 学习笔记 32 ~ 33
Effective STL 学习笔记 32 ~ 33 */--> div.org-src-container { font-size: 85%; font-family: monospace; ...
- SSIS 学习之旅 FTP文件传输-FTP任务
这一章主要讲解一下FTP控件. 设计: 通过Demon库的Users表数据生成CSV文件. 生成后的CSV文件抛送到FTP指定目录下. 其他控件的使用这里就不做详细讲解了.大家如果有不懂得可以 ...
- 一步一步学习IdentityServer4 (5) .NETCore2.0 Swagger
首先添加nuget: Swashbuckle.AspNetCore services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new ...
- CCF CSP 201709-4 通信网络
CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201709-4 通信网络 问题描述 某国的军队由N个部门组成,为了提高安全性,部门之间建立了M ...