【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 ...
随机推荐
- python学习之argparse模块的使用
以下内容主要来自:http://wiki.jikexueyuan.com/project/explore-python/Standard-Modules/argparse.html argparse ...
- Python基础之多线程事件Event
import threading,time class Boss(threading.Thread): def run(self): print("BOSS:伙计们今晚上加班到22:00&q ...
- 使用Cache缓存
存放位置:服务器内存,用于频繁访问且不轻易更改的内容缓存. string CacheKey = "CT1"; //检索指定项, object objModel = Cache.Ge ...
- C#利用System.Net发送邮件
啥也不说了,直接上干货 using System.Net.Mail;using System.Net; //使用发送邮件的邮箱 var emailAcount = "826217795@qq ...
- MVVM模式用依赖注入的方式配置ViewModel并注册消息
最初的想法 这次主要讨论下给View指定ViewModel的事情.一般来说给View指定ViewModel常用的方式有两种,一种是在View的后台代码中写DataContext = new ViewM ...
- wordcount在本地运行报错解决:Exception in thread "main" java.lang.UnsatisfiedLinkError:org.apache.hadoop.io.native.NativeID$Windows.access
在windows中的intellij中运行wordcount程序,控制台输出以下报错 在Intellij编辑器中解决办法:本地重新创建NativeIO类,修改一个方法返回值,然后用新建的NativeI ...
- mac系统安装redis
1.下载 打开官网:https://redis.io/ Download---Stable---Download3.2.8,下载最新稳定版,这里是3.2.8 2.安装 下载完成后,打开命令行工具,执行 ...
- Linux入门(二)Shell基本命令
上一篇讲了普通用户切换到root用户,今天补充一点,对于Debian和Ubuntu用户,安装时候只有一个普通用户注册,在需要root权限时,我们可以在普通用户模式下输入sudo这个命令运行某些相关特权 ...
- Unix IPC之读写锁
linux中读写锁的rwlock介绍 读写锁比mutex有更高的适用性,可以多个线程同时占用读模式的读写锁,但是只能一个线程占用写模式的读写锁: 1,当读写锁是写加锁状态时, 在这个锁被解锁之前, 所 ...
- kickstart配置LINUX无人值守选项--rootpw
linux kickstart rootpw密码可以使用明文,也可以使用加密过的值(密码为:IPPBXADMINROOT) 注意:在这里要使用加密过的值,否则安全性就太低了 rootpw --iscr ...