F
题意:一些原料生产一些产品,给出反应方程、初始原料的量,求反应的瓶颈。
思路:看出是一个DAG图,对于每个反应,反应物向生成物连边,反应物的系数为边权,生成五系数为点权。f[i]表示从原料到i点的(边权/点权)的乘积。f[i] / i的点权/ i的初始量的最大值即为答案。
from fractions import Fraction as F
if __name__ == '__main__':
n,m = [int(x.strip()) for x in input().split()]
node_id = {}
node = []
node_cnt = []
edge_v = [ [] for i in range(n) ]
edge = [ [] for i in range(n) ]
node_v = [F(1) for i in range(n)]
vist = [0 for i in range(n)]
for i in range(0, n):
x,v = input().strip().split()
node_id[x] = i
node.append(x)
node_cnt.append(int(v))
for i in range(0, m):
s = [x.strip() for x in input().split()]
s = [x for x in s if x !='+' and x != '=']
x = s[-1]
x_cnt = F(s[-2])
node_v[node_id[x]] = x_cnt
for j in range(0, len(s) - 2, 2):
y_cnt = F(s[j])
y = s[j + 1]
edge[node_id[x]].append(node_id[y])
edge_v[node_id[x]].append(y_cnt)
vist[node_id[y]] = vist[node_id[y]] + 1
que =[]
f = [F(0)]*n
for i in range(n):
f.append(0)
for i in range(n):
x = node[i]
if vist[i] == 0:
que.append(i)
f[i] = F(1)
while not que == []:
x_id = que[-1]
que.pop()
for i in range(0, len(edge[x_id])):
y_id = edge[x_id][i]
f[y_id] = f[y_id] + f[x_id] * edge_v[x_id][i] / node_v[x_id]
vist[y_id] = vist[y_id] - 1
if vist[y_id] == 0:
que.append(y_id)
maxa = f[0] / node_cnt[0] /node_v[0]
cnt = 0
ans = []
for i in range(n):
if f[i] / node_cnt[i] / node_v[i] > maxa:
maxa = f[i] / node_cnt[i] / node_v[i]
for i in range(n):
if f[i] / node_cnt[i] / node_v[i] == maxa:
ans.append(node[i])
cnt = cnt + 1
ans = sorted([ans[x] for x in range(cnt)])
print(cnt)
print(*ans)
G
题意:每张牌有四个属性,每个属性有3个状态。如果三张牌满足以下条件,则可以构成一个合法集合:对于任意一种属性,要么三张牌的状态互不相同,要么完全相同。给出N(
N
≤
256
N \leq 256
N≤256)张牌,某些牌某写属性可能为”?“(万能状态,可以当成任意一种状态),求是否存在合法的集合。
思路:
考场AC:
记录f [0 / 1 / 2 / 3 ] [0 / 1 / 2 / 3] [0 / 1 / 2 / 3] [0 / 1 / 2 / 3] 记录每种牌有多少,其中f[0][x][x][x]表示第一种属性任意的牌,即f[0][x][x][x] = f[1][x][x][x]+f[2][x][x][x]+f[3][x][x]。
枚举两张牌,选第三张牌。对于每一种属性,如果两张牌有至少一个万能状态,那么第三张牌任意;否则,如果一样,第三张牌为相同状态,如果不一样,第三张牌为第三种状态。这样就唯一确定了第三张牌的所有状态。(注意,包括”任意“状态)
处理第三张牌的集合:
记录f [0 / 1 / 2 / 3 ] [0 / 1 / 2 / 3] [0 / 1 / 2 / 3] [0 / 1 / 2 / 3] 记录每种牌有多少,其中f[0][x][x][x]表示第一种属性任意的牌,即f[0][x][x][x] = f[1][x][x][x]+f[2][x][x][x]+f[3][x][x]。
每张牌最多能满足256种状态,所以暴力更新f就可以。
时间复杂度 O(
T
N
2
TN^{2}
TN2)
出题人正解:
21张牌种必有一个合法集合……所以,O(
2
1
3
21^{3}
213)暴力枚举做就好了……
H
题意:给一些字符串,每个字符串无限循环,求所有无限循环字符串的公共子串的大小。如果公共子串无限循环,输出-1。
思路:
对于每个给出的串,先求其本质串(用kmp去循环节),用最小表示法表示。
如果所有串变为一个,那么输出-1。
否则,将所有串倍长4倍,在sam上跑就好。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
const int N = 3e6 + 10;
string str[N];
char s[N];
int ch[N][26],fa[N],len[N*8];
int last=1,tot=1;
void insert(int x)
{
int p=last;
int np=last=++tot;
len[np]=len[p]+1;
while(p&&!ch[p][x]) { ch[p][x]=np; p=fa[p]; }
if(!p) fa[np]=1;
else
{
int q=ch[p][x];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot;
for(int i=0;i<26;i++) ch[nq][i]=ch[q][i];
fa[nq]=fa[q]; fa[np]=fa[q]=nq;
len[nq]=len[p]+1;
while(p&&ch[p][x]==q)
{ ch[p][x]=nq; p=fa[p]; }
}
}
}
int fail[N];
int get_fail()
{
int l = strlen(s + 1);
fail[1] = 0;
int j = 0;
for(int i = 2; i <= l; i++)
{
fail[i] = 0;
while(j && s[j + 1] != s[i])
j = fail[j];
if(s[j + 1] == s[i])
fail[i] = ++j;
}
if(l % (l - fail[l])) return l;
return l - fail[l];
}
int get_min(int l)
{
int i = 0, j = 1, k = 0;
while(i < l && j < l && k < l)
{
if(s[(i + k) % l + 1] == s[(j + k) % l + 1])
k++;
else
{
if(s[(i + k) % l + 1] > s[(j + k) % l + 1])
i = i + k + 1;
else
j = j + k + 1;
if(i == j)
i++;
k = 0;
}
}
return min(i + 1, j + 1);
}
int mx[N], mi[N], r[N];
void solve(int id)
{
for(int i = 1; i <=tot; i++)
mx[i] = 0;
int l = str[id].size();
int p=1,now=0;
for(int i = 0; i < l; i++)
{
int x = str[id][i] - 'a';
if(ch[p][x])
{ now++; p = ch[p][x];}
else
{
while(p&&!ch[p][x]) p = fa[p];
if(!p){p = 1;now = 0;}
else
{ now = len[p] + 1; p = ch[p][x];}
}
mx[p] = max(mx[p], now);
}
for(int i = 1; i <= tot; i++)
{
int x = r[i];
mx[fa[x]] = max(mx[fa[x]], mx[x]);
mi[x] = min(mi[x], mx[x]);
}
}
int cmp (int x, int y)
{
return len[x] > len[y];
}
int main()
{
int n, l, p;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%s", s + 1);
l = get_fail();
p = get_min(l);
for(int j = p; j <= l; j++)
str[i] += s[j];
for(int j = 1; j < p; j++)
str[i]+= s[j];
}
sort(str + 1, str + n + 1);
int nn = unique(str + 1, str + n +1) - str - 1;
if(nn == 1)
{
printf("-1\n");
return 0;
}
n = nn;
int minlen = min(str[1].size(), str[2].size());
int maxlen = max(str[1].size(), str[2].size());
if(str[1].size() == minlen) p = 1;
else p = 2;
for(int i = 3; i <= n; i++)
{
l = str[i].size();
maxlen = min(maxlen, l);
if(maxlen < minlen) swap(maxlen, minlen);
if(l == minlen) p = i;
}
string sstr = str[p];
while(str[p].size() < 4 * maxlen)
str[p] += sstr;
minlen = str[p].size();
for(int i = 1; i <= n; i++)
if(i != p)
{
str[i]+=str[i];
str[i]+=str[i];
if(str[i].size() < minlen)
{
minlen = str[i].size();
p = i;
}
}
l = str[p].size();
last = 1;
for(int i = 0; i < l; i++)
insert(str[p][i] - 'a');
for(int i = 1; i <= tot; i++)
r[i] = i, mi[i] = len[i];
sort(r + 1, r + tot + 1, cmp);
for(int i = 1; i <= n; i++)
if(i != p)
solve(i);
LL ans = 0;
for(int i = 2; i <= tot; i++)
ans += max(0, mi[i] - len[fa[i]]);
cout<<ans<<endl;
return 0;
}