https://atcoder.jp/contests/abc264/tasks
F - Monochromatic Path
给定 h行 w列 黑/白 格子
可以 支付R[i] 翻转第i行
可以 支付C[j] 翻转第j列
求最小代价,让从(1,1)到(h,w) 有一条同色只向下/右走的路径
范围
h,w 2000
ri,ci 1e9
2s
1024mb
我的思路
然后 记走到 dp[i][j][i行是否翻转 0/1][j列是否翻转0/1]
的最小代价
dp[i][j][计算出是否翻转][sj] = dp[i-1][j][0/1][sj]
因为知道[i-1][j]
的颜色和j
的翻转状态
dp[i][j][si][计算出是否翻转] = dp[i][j-1][si][0/1]
因为知道[i][j-1]
的颜色和i
的翻转状态
那么 就没了?
代码
https://atcoder.jp/contests/abc264/submissions/35595592
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include <bits/stdc++.h> using namespace std; typedef long long ll; #define _(i,n) for (ll i=0;i<(ll)n;i++) ll read(){ll r;scanf("%lld",&r);return r;}
ll dp[2010][2010][2][2]; const ll INF=0x3f3f3f3f3f3f3f3f; ll r[2010]; ll c[2010];
int a[2010][2010]; char s[2010]; void setMin(ll&a,ll b){a=min(a,b);}
int main(){ int h=read(); int w=read(); _(i,h)r[i]=read(); _(j,w)c[j]=read(); _(i,h){ scanf("%s",s); _(j,w)a[i][j]=s[j]-'0'; } _(i,h)_(j,w)_(x,2)_(y,2)dp[i][j][x][y]=INF; _(x,2)_(y,2)dp[0][0][x][y]=x*r[0]+y*c[0]; _(i,h)_(j,w)_(x,2)_(y,2){ ll cost=dp[i][j][x][y]; if(cost==INF)continue; int color=a[i][j]^x^y; if(i+1<h){ int rev=color^a[i+1][j]^y; setMin(dp[i+1][j][rev][y],cost+rev*r[i+1]); } if(j+1<w){ int rev=color^a[i][j+1]^x; setMin(dp[i][j+1][x][rev],cost+rev*c[j+1]); } } ll ans=INF; _(x,2)_(y,2)setMin(ans,dp[h-1][w-1][x][y]); printf("%lld\n",ans); return 0; }
|
G - String Fair
小写字母非空字符串S的得分 = n个标准的得分和
第i个标准的得分 = S中字符串Ti作为连续子串 出现的次数
乘上Pi
输出最大的S的得分
如果可以无限大 输出Infinity
范围
n 18278=26*(1+26*(1+26))
1 <= |Ti| <= 3, 两两不等
2s
1024mb
我的思路
其实, 如果能找到一个循环节
(…)(…)
然后每个循环单位的贡献和为正, 则可以无限大
比如 (a,b,z) => 循环单位是 (abz,bza,zab,ab,bz,za,a,b,z)
(a,b) => 循环单位是 (ab,ba,a,b)
从这个角度,似乎比较难推出 至少要多少长度, 才能让”有正循环节”出现
考虑所有长1的里,如果有正贡献, 则用它即可
否则, 所有长1 贡献为0或负
那么如果长2里有正贡献 (ab) 且 (ab+ba+a+b > 0) 则选它
否则, 所有长1, a<=0, 长2的 (ab+ba+a+b<=0)
似乎 也不太行
在就是直接建图
每个3个字符一个节点
(a,b,c) -> (b,c,d) 有一条边其中代价为 d+cd+bcd
其中还有 (空,空,空), (空,空,c),(空,b,c) 这种点
那么问题是从(空,空,空) 出发的最大值
那么问题变成 判断是否有正环, 若没有求最大的
考虑说 值+距离来做??, 大概像spfa那样
感觉上是对的,没有数学证明
代码
https://atcoder.jp/contests/abc264/submissions/35596046
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i,a,n) for (ll i=a;i<(ll)n;i++) ll read(){ll r;scanf("%lld",&r);return r;}
const int N=27; pair<ll,int> f[N][N][N]; const ll INF=0x3f3f3f3f3f3f3f3f; char t[10]; ll cost[N*N*N+10]; int enc(int a,int b,int c){return c+N*(b+N*a);} int main(){ int n=read(); rep(i,0,n){ char*s=t+2; rep(j,0,2)t[j]=0; scanf("%s",s); int sz=strlen(s); rep(j,0,sz)s[j]-='a'-1; cost[enc(t[sz-1],t[sz],t[sz+1])]=read(); } rep(i,0,N)rep(j,0,N)rep(k,0,N)f[i][j][k]={-INF,-1}; ll ans=-INF; vector<pair<pair<ll,int>,tuple<int,int,int> > > smabc={{{0,-1},{0,0,0}}}; rep(i,0,smabc.size()){ auto [sm,abc]=smabc[i]; auto [s,m]=sm; auto [a,b,c]=abc; if(f[a][b][c].first > s)continue; if(enc(a,b,c)!=0)ans=max(ans,s); if(m==enc(a,b,c)){ printf("Infinity\n"); return 0; } rep(ch,1,N){ ll nc = s+cost[enc(b,c,ch)]; if(c) nc +=cost[enc(0,0,ch)]; if(b) nc +=cost[enc(0,c,ch)]; if(f[b][c][ch].first<nc)smabc.push_back({f[b][c][ch]={nc,max(m,enc(a,b,c))},{b,c,ch}}); } } printf("%lld\n",ans); return 0; }
|
Ex - Perfect Binary Tree
n点根为1的树
对于k=1..n
问 在 [1..k] 中选 含点1 的 点集, 有多少 按照祖先关系导出图 满足 生成的是一个2^d-1节点的 完美满二叉树(节点要么是叶子,要么两个子节点, 所有叶子在最后一层)
就是 导出图就是, 如果u和v都被选了,且原图u,v有边,则新图里也有边
范围
n 3e5
3s
1024mb
我的思路
啊, 就树上dp?
dp[u为根][dep]
= {map[最大出现的值] = 方案数}
但似乎这样最大出现的值 会 让状态数很多
随着k增大 从下向上更新
因为pi < i
所以每次 到k时 多出来的所有合法方案,一定是含有k的(其实题目样例1的解释里 有一点这个想法的感觉
所以每次选了k, 让当前位置 方案+1
然后向上算
u时 方案增加t
那么fa(u) 方案增加 t*(count[fa(u)][dep] - count[u][dep-1])
,再向上, 当为0时退出,
而又注意到要生成的是 完美满二叉树, 所以 不会超过 log(2,3e5) < 20
似乎就可以做了?
代码
https://atcoder.jp/contests/abc264/submissions/35596646
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <bits/stdc++.h> #include <atcoder/modint> using mint = atcoder::modint998244353; using namespace std;
typedef long long ll; #define rep(i,a,n) for (ll i=a;i<(ll)n;i++) ll read(){ll r;scanf("%lld",&r);return r;}
const int PWR=22;
int fa[300010]; mint cnt[300010][PWR]; mint sum[300010][PWR];
mint toroot(int u,int d,mint x){ if(d==PWR)return 0; if(u==1){ cnt[u][d]+=x; return x; } int v=fa[u]; mint inc = x*(sum[v][d+1]-cnt[u][d]); sum[v][d+1]+=x; cnt[u][d] +=x; return toroot(v,d+1,inc); }
int main(){ int n = read(); rep(i,2,n+1) fa[i]=read(); mint ans=0; rep(k,1,n+1){ ans+=toroot(k,0,1); printf("%d\n",ans.val()); } return 0; }
|
总结
F
没啥难的
G
虽然糊过了, 但总感觉上面有点问题, 这样做判断环 ,会不会多个增在环上跑, 导致 O(并行的bfs指针 * 环长)的复杂度
还是应该搞个maxPQ?
Ex
emmm, 过了个橙题, 虽然我还是觉得G更难点
参考
官方题解