2020.3.14 解题报告
Posted luckyblock
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020.3.14 解题报告相关的知识,希望对你有一定的参考价值。
[ ext{最低分开心.}]
[ ext{By:Luckyblock233}]
答题情况
总成绩 : 15 , 排名 : /
T1 : 5 T2 : 10 T3 : 0
各题目分析
题目 1 :
预估成绩 : 20 实际成绩 : 5 考试用时 : 8:00 ~ 11:20
看完题之后发现只有这道有思路,
大部分时间都放在这道题上了。
由于样例有误 浪费了1h的时间。
题目 2 :
预估成绩 : 20 实际成绩 : 10 考试用时 : 11:20 ~ 12:00
写了爆搜,
由于对题目模型理解不是很透彻,在调试时费了比较多的时间。
题目 3 :
预估成绩 : 0 实际成绩 : 0 考试用时 :
没有读懂题目,决定放弃该题目。
题目解析
T1 :
AC自动机 + DP。
将n个数建成AC自动机,求权值相当于多串匹配。
我们要求[??, ??]之间满足条件的数的个数,
等价于求[1, ??]和[1, ?? ? 1]内满足条件的数的个数,然后相减即可。
这样问题就变成了一个经典的数位 DP 问题,记??_????????
表示我们现在已经填好了这个数的前??位同时走到了 AC 自动机的第??个状态,当前的价值是??的方案数。
??为0/1变量,表示当前数是等于上界值还是小于上界值,
转移的方法枚举下一位所填的数即可。
T2 :
20分:
N*M <= 30。
暴力搜索枚举每种状况,判断是否合法。
数据范围中除了??以外的所有变量的值都非常小,所以显然我们要从剩下的变量入手。
我们考虑到使用类似于状态压缩动态规划的方法,我们可以对按列进行转移,
用 (?? + 1)^?? × ??个状态表示每一行当前有多少个连续的男生并且已经有多少列是全部都是男生。
考虑到这个值在极限情况下会达到上万级别,我们无法使用矩阵乘法进行转移.
所以我们需要减少状态的数量。
注意到我们实际上并不需要知道每一行当前有多少个连续的男生,
我们需要的只有有多少行连续的男生是0,1, ? , ??个,
这样便等价于将??有序拆分成?? + 1个整数的和的方案数。
这样做之后,在极限状态下的状态数的个数也不超过200个,便可以使用矩阵乘法顺利转移。
T3 :
题目大意:求最小状态数的 DFA 的状态数.
以先随意构造一个无环的DFA,这个 DFA 就是我们的 trie 树.
我们需要减少 trie 树的状态,即合并某些状态。
两个状态是能合并的,当且仅当这两个状态在 trie 树上对应的子树是完全一致的,
这里我们可以使用类似于字符串的方法,对每一棵子树进行哈希,
将其子树的每一条边延伸出去的子树的哈希值从小到大排序之后看作一个新的字符串进行哈希.
代码实现
T1 :
考场代码
//知识点:AC自动机
/*
By:Luckyblock
*/
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctype.h>
#define max std::max
#define ll long long
const int MARX = 1010;
const int mod = 1e9 + 7;
//=============================================================
int N, M, K, lth, cnt, match[MARX], Q[MARX];
int L, R, Ans, Val[MARX];
char s[MARX], t[MARX];
int NodeNum, tr[MARX][25], fail[MARX], size[MARX];
//=============================================================
inline int read()
{
int f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Insert(char *s, int id)
{
int u = 0;
for(int i = 1; s[i]; u = tr[u][s[i]], i ++)
if(! tr[u][s[i]]) tr[u][s[i]] = ++ NodeNum;
match[id] = u;
}
void Build()
{
std :: queue <int> q;
while(! q.empty()) q.pop();
for(int i = 1; i <= M; i ++)
if(tr[0][i]) q.push(tr[0][i]);
while(! q.empty())
{
int u = q.front(); q.pop();
Q[++ cnt] = u;
for(int i = 1; i <= M; i ++)
if(tr[u][i]) fail[tr[u][i]] = tr[fail[u]][i], q.push(tr[u][i]);
else tr[u][i] = tr[fail[u]][i];
}
}
void Query(char *t)
{
for(int i = 1, u = 0; t[i]; i ++)
u = tr[u][t[i]], size[u] ++;
for(int i = NodeNum; i >= 0; i --) size[fail[Q[i]]] += size[Q[i]];
}
int qpow(int x, int y)
{
int ret = 1;
while(y)
{
if(y & 1) ret *= x;
x *= x, y >>= 1;
}
return ret;
}
void GetT(int Val)
{
if((int) (Val / M) == 0){t[++ lth] = (char) (Val + 1); return ;}
GetT(Val / M);
t[++ lth] = (char) (Val % M + 1);
}
//=============================================================
signed main()
{
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
N = read(), M = read(), K = read();
for(int lth1 = read(), i = lth1 - 1; i >= 0; i --) L += read() * qpow(M, i);
for(int lth2 = read(), i = lth2 - 1; i >= 0; i --) R += read() * qpow(M, i);
for(int i = 1; i <= N; i ++)
{
char s[210] = {0};
for(int j = 1, lth = read(); j <= lth; j ++) s[j] = (char) (read() + 1);
Val[i] = read(); Insert(s, i);
}
Build();
for(int i = L, Sum; i <= R; i ++)
{
lth = Sum = 0; GetT(i); t[++ lth] = 0;
memset(size, 0, sizeof(size));
Query(t);
for(int i = 1; i <= N; i ++) Sum += size[match[i]] * Val[i];
if(Sum <= K) Ans ++;
}
printf("%d
", Ans);
return 0;
}
正解
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define inc(a,b) {a+=b;if (a>=mo) a-=mo;}
#define newnode ++wmt
const int maxn=210;
const int maxm=22;
const int maxk=510;
const int maxp=210;
const int mo=1000000007;
int n,m,k,llen,rlen,wmt,root,f[maxn][maxp][maxk][2],l[maxn],r[maxn],s[maxn],q[maxp];
struct node
{
int next[maxm];
int value,fail;
node()
{
memset(next,0,sizeof(next));
value=fail=0;
}
}z[maxp];
void insert(int *s,int l,int v)
{
int p=root;
for (int a=1;a<=l;a++)
{
if (!z[p].next[s[a]]) z[p].next[s[a]]=newnode;
p=z[p].next[s[a]];
}
z[p].value+=v;
}
void build_AC()
{
int front=1,tail=1;
q[1]=root;
for (;front<=tail;)
{
int now=q[front++];
for (int a=0;a<m;a++)
if (z[now].next[a])
{
int p=z[now].fail;
while (p)
{
if (z[p].next[a])
{
z[z[now].next[a]].fail=z[p].next[a];
break;
}
p=z[p].fail;
}
if (!p) z[z[now].next[a]].fail=root;
z[z[now].next[a]].value+=z[z[z[now].next[a]].fail].value;
q[++tail]=z[now].next[a];
}
else
{
z[now].next[a]=z[z[now].fail].next[a];
if (!z[now].next[a]) z[now].next[a]=root;
}
}
}
int solve(int *y,int l)
{
if (!l) return 0;
int ans=0;
for (int a=1;a<l;a++)
for (int b=1;b<=wmt;b++)
for (int c=0;c<=k;c++)
inc(ans,f[a][b][c][0]);
for (int a=l;a>=1;a--)
for (int b=1;b<=wmt;b++)
for (int c=(a==l);c+(a!=1)<=y[a];c++)
{
int p=b,delta=0;
p=z[p].next[c];
delta+=z[p].value;
for (int d=a+1;d<=l;d++)
{
p=z[p].next[y[d]];
delta+=z[p].value;
}
for (int d=0;d<=k-delta;d++)
for (int e=0;e<=1;e++)
inc(ans,f[a-1][b][d][e]);
}
return ans;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
scanf("%d",&llen);
for (int a=llen;a>=1;a--)
scanf("%d",&l[a]);
scanf("%d",&rlen);
for (int a=rlen;a>=1;a--)
scanf("%d",&r[a]);
root=newnode;
for (int a=1,v;a<=n;a++)
{
int l;
scanf("%d",&l);
for (int b=1;b<=l;b++)
scanf("%d",&s[b]);
reverse(s+1,s+l+1);
scanf("%d",&v);
insert(s,l,v);
}
build_AC();
n=max(llen,rlen);
f[0][root][0][0]=1;
for (int a=0;a<n;a++)
for (int b=1;b<=wmt;b++)
for (int c=0;c<=k;c++)
for (int d=0;d<=1;d++)
if (f[a][b][c][d])
for (int e=0;e<m;e++)
if (c+z[z[b].next[e]].value<=k) inc(f[a+1][z[b].next[e]][c+z[z[b].next[e]].value][e==0],f[a][b][c][d]);
l[1]--;
for (int a=1;a<=llen;a++)
if (l[a]<0) l[a]+=m,l[a+1]--;
else break;
if (!l[llen]) llen--;
printf("%d
",(solve(r,rlen)-solve(l,llen)+mo)%mo);
return 0;
}
T2:
考场代码
//
/*
By:Luckyblock
*/
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#define ll long long
//=============================================================
int N, M, P, Q, ans, cnt[30];
//=============================================================
inline int read()
{
int f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Dfs(int x, int y, int man)
{
if(x == N + 1)
{
int sum = 0;
for(int i = 1; i <= M; i ++) sum += (cnt[i] == N);
if(sum <= Q) ans ++; return ;
}
if(y == M)
{
Dfs(x + 1, 1, 0);
if(man < P - 1) cnt[y] ++, Dfs(x + 1, 1, 0), cnt[y] --;
return ;
}
Dfs(x, y + 1, 0);
if(man < P - 1) cnt[y] ++, Dfs(x, y + 1, man + 1), cnt[y] --;
}
//=============================================================
int main()
{
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
N = read(), M = read(), P = read(), Q = read();
Dfs(1, 1, 0);
printf("%d
", ans);
return 0;
}
正解
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef unix
#define FMT "%lld"
#else
#define FMT "%I64d"
#endif
typedef long long ll;
const int kMod = 1000000007;
const int kSize = 180;
int n, p, q;
ll m;
struct State {
int cnt[3], ill;
State() {}
State(int a, int b, int c, int d) {
cnt[0] = a, cnt[1] = b, cnt[2] = c, ill = d;
}
} state[kSize], cur;
int re[11][11][11][5], idx;
struct Matrix {
int v[kSize][kSize];
void clear() {
memset(v, 0, sizeof(v));
}
Matrix() {
clear();
}
} trans, init, e, ans, res, tmp;
#define INC(x, y) { (x) += (y); if ((x) >= kMod) (x) -= kMod; }
Matrix operator * (const Matrix &a, const Matrix &b) {
static Matrix c;
c.clear();
for (int i = 0; i < idx; ++ i)
for (int k = 0; k < idx; ++ k) if (a.v[i][k])
for (int j = 0; j < idx; ++ j)
INC(c.v[i][j], (ll)a.v[i][k] * b.v[k][j] % kMod);
return c;
}
void Power(ll b) {
while (b) {
if (b & 1) res = res * tmp;
b >>= 1;
tmp = tmp * tmp;
}
}
void Dfs(int step, int sum) {
if (step == p) {
if (!sum) {
for (int i = 0; i <= q; ++ i) {
cur.ill = i;
re[cur.cnt[0]][cur.cnt[1]][cur.cnt[2]][cur.ill] = idx;
state[idx ++] = cur;
}
}
}
else
for (int i = 0; i <= sum; ++ i) {
cur.cnt[step] = i;
Dfs(step + 1, sum - i);
}
}
int main() {
scanf("%d" FMT "%d%d", &n, &m, &p, &q);
memset(re, -1, sizeof(re)), idx = 0, Dfs(0, n);
for (int i = 0; i < idx; ++ i) {
State u = state[i];
for (int msk = (u.ill == q); msk < (1 << n); ++ msk) {
State v(0, 0, 0, u.ill + (msk == 0));
bool fail = false;
for (int k = 0; k < n && !fail; ++ k) {
int o = msk >> k & 1;
for (int r = 0, s = 0; r < p; ++ r) {
s += u.cnt[r];
if (k < s) {
if (o)
++ v.cnt[0];
else {
if (r + 1 == p)
fail = true;
else
++ v.cnt[r + 1];
}
break;
}
}
}
if (!fail) {
int j = re[v.cnt[0]][v.cnt[1]][v.cnt[2]][v.ill];
++ trans.v[j][i];
}
}
}
for (int i = 0; i < idx; ++ i) e.v[i][i] = 1;
res = e, tmp = trans;
Power(m - 1);
for (int msk = (q == 0); msk < (1 << n); ++ msk) {
State u(0, 0, 0, (msk == 0));
bool fail = false;
for (int k = 0; k < n && !fail; ++ k) {
int o = msk >> k & 1;
if (o)
++ u.cnt[0];
else {
if (1 == p)
fail = true;
else
++ u.cnt[1];
}
}
if (!fail) {
int i = re[u.cnt[0]][u.cnt[1]][u.cnt[2]][u.ill];
init.v[i][0] ++;
}
}
ans = res * init;
int sum = 0;
for (int i = 0; i < idx; ++ i)
INC(sum, ans.v[i][0]);
printf("%d
", sum);
return 0;
}
T3:
正解
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define Hash unsigned long long
const int maxn=5010;
const Hash bit=10007;
int n,wmt,root,y[maxn*30],f[maxn*30];
char s[200];
struct node
{
int next[26],son,depth;
Hash hash;
bool end;
}z[maxn*35];
bool cmp1(int a,int b)
{
return z[a].depth<z[b].depth;
}
bool cmp2(int a,int b)
{
if (z[a].son!=z[b].son) return z[a].son<z[b].son;
else return z[a].hash<z[b].hash;
}
inline int newnode()
{
wmt++;
z[wmt].end=false;
z[wmt].hash=0;
z[wmt].depth=0;
z[wmt].son=0;
memset(z[wmt].next,0,sizeof(z[wmt].next));
return wmt;
}
void insert()
{
int l=strlen(s+1);
int p=root;
for (int a=1;a<=l;a++)
{
if (!z[p].next[s[a]-'a']) z[p].next[s[a]-'a']=newnode(),z[p].son++;
p=z[p].next[s[a]-'a'];
}
z[p].end=true;
}
void dfs(int p)
{
for (int a=0;a<26;a++)
if (z[p].next[a]) dfs(z[p].next[a]),z[p].depth=max(z[p].depth,z[z[p].next[a]].depth);
z[p].depth++;
}
int getf(int now)
{
if (now==f[now]) return now;
else return f[now]=getf(f[now]);
}
void Union(int p1,int p2)
{
int f1=getf(p1);
int f2=getf(p2);
f[f1]=f2;
}
int main()
{
scanf("%d",&n);
wmt=0;
root=newnode();
for (int a=1;a<=n;a++)
{
scanf("%s",s+1);
insert();
}
dfs(root);
for (int a=1;a<=wmt;a++)
y[a]=a;
sort(y+1,y+wmt+1,cmp1);
int ans=wmt;
for (int a=1;a<=wmt;a++)
f[a]=a;
for (int a=1;a<=wmt;)
{
int b=a;
while (b<=wmt && z[y[b]].depth==z[y[a]].depth)
b++;
b--;
for (int c=a;c<=b;c++)
{
z[y[c]].hash=z[y[c]].end+1;
for (int d=0;d<26;d++)
{
int p;
if (z[y[c]].next[d]) p=getf(z[y[c]].next[d])+4;
else p=3;
z[y[c]].hash=z[y[c]].hash*bit+p;
}
}
sort(y+a,y+b+1,cmp2);
for (int c=a;c<=b;)
{
int d=c;
while (d<=b && z[y[d]].son==z[y[c]].son && z[y[d]].hash==z[y[c]].hash)
d++;
d--;
ans-=d-c;
for (int e=c+1;e<=d;e++)
Union(y[c],y[e]);
c=d+1;
}
a=b+1;
}
printf("%d
",ans);
return 0;
}
以上是关于2020.3.14 解题报告的主要内容,如果未能解决你的问题,请参考以下文章
2020/3/14 Preliminaries for Benelux Algorithm Programming Contest 2019 部分补题报告和解题报告