2026-01-02~01-04 hetao1733837 的刷题笔记
01-02
HDU2476 String painter
原题链接:String painter
分析
很不错的巩固题,先从 A A A 为空白串考虑,枚举 B B B 串区间左右端点,相同则直接刷一遍,再加中间之前统计的答案。否则,枚举断点。
同理初始不为空白串,当 A A A, B B B 串当前位置相同时,直接从之前算好的转移;否则,枚举断点更新。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int dp[N][N];
char a[N], b[N];
int ans[N];
int main(){
while (~scanf("%s%s", a + 1, b + 1)){
int n = strlen(a + 1);
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; i++){
dp[i][i] = 1;
}
for (int len = 2; len <= n; len++){
for (int l = 1; l <= n - len + 1; l++){
int r = l + len - 1;
if (b[l] == b[r]){
dp[l][r] = min(dp[l][r], dp[l + 1][r]);
}
for (int k = l; k < r; k++){
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]);
}
}
}
memset(ans, 0x3f, sizeof(ans));
ans[0] = 0;
for (int i = 1; i <= n; i++){
if (a[i] == b[i]) {
ans[i] = ans[i - 1];
}
else{
for (int j = 1; j <= i; j++) {
ans[i] = min(ans[i], ans[j - 1] + dp[j][i]);
}
}
}
cout << ans[n] << '\n';
}
return 0;
}
HDU1003 Max Sum
原题链接:Max Sum
分析
两遍 D P DP DP,反向跑的,不难理解。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int T;
int n, a[N], dp[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> T;
for (int cs = 1; cs <= T; cs++){
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
dp[1] = a[1];
for (int i = 2; i <= n; i++){
if (dp[i - 1] < 0)
dp[i] = a[i];
else
dp[i] = dp[i - 1] + a[i];
}
int maxn = dp[1], l, r = 1;
for (int i = 1; i <= n; i++){
if (dp[i] > maxn){
maxn = dp[i];
r = i;
}
}
int tot = 0;
for (int i = r; i >= 1; i--){
tot += a[i];
if (tot == maxn){
l = i;
}
}
cout << "Case " << cs << ":" << '\n';
cout << maxn << " " << l << " " << r << '\n';
if (cs != T)
cout << '\n';
}
}
CF207C3 Game with Two Trees
原题链接:Game with Two Trees
分析
被 aoao 催了……
涌现出字典树、树剖等奇怪的东西……
字典树显然是对的。
新增节点不会影响之前的,这是显然的。
aoao 给出了离线,这个是个好方向啊……也就是说,我可以把整棵树建出来,是不是要上点树上的东西?不吧,并不合理。
离线难道是为了变加边为删边吗?
是对的,但是,咋做,我还是不会……
操,这个真的天才!
进行一步转化,由于对于 T 1 T1 T1 是反串,不妨做一下翻转,即 T1 的一条根链与 T2 的一条向上的链匹配。
这一步可以哈希+倍增,我不会
然后,对于删除,如果删除的是 T 1 T1 T1 的点,影响了 T 2 T2 T2 的整个子树;
如果删除的是 T 2 T2 T2 的点,影响的是 T 1 T1 T1 的某条根链。
即单点加链求和+单点加子树求和,上树剖!
时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)。
建树小细节就是:把所有本质相同的点压到一起。这样就形成了真正的字典树。
明天再写,先回宿舍了……
回归!开整 6KB 超长代码!
挂一下aoao的博客。
彻底失去耐心……
正解
#include<bits/stdc++.h>
#define ll long long
#define N 300005
#define K 19
#define B1 131
#define B2 13331
#define mod1 998244353
#define mod2 998244853
#define pii pair<int,int>
using namespace std;
bool ed;
int Tc=1,Q,n1,n2,pw1[N<<1],pw2[N<<1];
ll res[N<<1];
int id1[N],id2[N];
pii lsh[N];
void init(int n=600000){
pw1[0]=pw2[0]=1;
for(int i=1;i<=n;i++){
pw1[i]=1ll*pw1[i-1]*B1%mod1;
pw2[i]=1ll*pw2[i-1]*B2%mod2;
}
}
struct qry{
int t,u;
char c;
}q[N<<1];
struct trie{
int tr[N][26],val[N],fa[N],tp[N];
int idx=1;
inline int ins(int p,char c){
int t=c-'a';
if(!tr[p][t])tr[p][t]=++idx;
fa[tr[p][t]]=p;
p=tr[p][t];
val[p]++;
tp[p]=c-'a';
return p;
}
}t1,t2;
map<pii,int>mp;
int aoao;
void dfs1(int u,int hsh1,int hsh2){
lsh[++aoao]={hsh1,hsh2};
mp[make_pair(hsh1,hsh2)]=u;
for(int i=0;i<26;i++){
if(t1.tr[u][i]){
dfs1(t1.tr[u][i],((1ll*hsh1*B1)%mod1+i+1)%mod1,((1ll*hsh2*B2)%mod2+i+1)%mod2);
}
}
}
int f[K][N],g1[K][N],g2[K][N];
void dfs2(int u,int fa,int las){
f[0][u]=fa;
g1[0][u]=g2[0][u]=las;
for(int i=1;i<K;i++){
f[i][u]=f[i-1][f[i-1][u]];
g1[i][u]=((1ll*g1[i-1][u]*pw1[1<<(i-1)])%mod1+g1[i-1][f[i-1][u]])%mod1;
g2[i][u]=((1ll*g2[i-1][u]*pw2[1<<(i-1)])%mod2+g2[i-1][f[i-1][u]])%mod2;
}
for(int i=0;i<26;i++){
if(t2.tr[u][i]){
dfs2(t2.tr[u][i],u,i+1);
}
}
}
int id[N],cnt[N];
ll sum;
void dfs(int u){
if(cnt[u])sum-=cnt[u];
for(int i=0;i<26;i++){
if(t1.tr[u][i]){
dfs(t1.tr[u][i]);
}
}
}
struct ds{
vector<int>e[N];
void add(int a,int b){
e[a].push_back(b);
}
void dfs(int u,int fa){
t1.val[u]+=t1.val[fa];
for(auto j:e[u]){
if(j==fa)continue;
dfs(j,u);
}
}
int dep[N],fa[N],siz[N],son[N],top[N];
int dfn[N],nw[N],ts;
void dfs1(int u,int f){
dep[u]=dep[f]+1;
fa[u]=f;
siz[u]=1;
for(auto j:e[u]){
if(j==fa[u])continue;
dfs1(j,u);
siz[u]+=siz[j];
if(siz[j]>siz[son[u]])son[u]=j;
}
}
void dfs2(int u,int f){
top[u]=f;
dfn[u]=++ts;
nw[ts]=u;
if(son[u])dfs2(son[u],f);
for(auto j:e[u]){
if(j==fa[u]||j==son[u])continue;
dfs2(j,j);
}
}
struct node{
ll val,cnt,tag,len;
}tr[N<<2];
void pushup(int u){
tr[u].cnt=tr[u<<1].cnt+tr[u<<1|1].cnt;
tr[u].val=tr[u<<1].val+tr[u<<1|1].val;
}
void build(int u,int l,int r){
tr[u].len=r-l+1;
if(l==r){
tr[u].cnt=cnt[nw[l]];
tr[u].val=t1.val[nw[l]];
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void maketag(int u,ll v){
tr[u].val+=tr[u].len*v;
tr[u].tag+=v;
}
void pushdown(int u){
if(!tr[u].tag)return;
maketag(u<<1,tr[u].tag);
maketag(u<<1|1,tr[u].tag);
tr[u].tag=0;
}
void modify1(int u,int l,int r,int L,int R,ll v){
if(l>=L&&r<=R){
maketag(u,v);
return;
}
pushdown(u);
int mid=l+r>>1;
if(L<=mid)modify1(u<<1,l,mid,L,R,v);
if(R>mid)modify1(u<<1|1,mid+1,r,L,R,v);
pushup(u);
}
void modify2(int u,int l,int r,int p,ll v){
if(l==r){
tr[u].cnt+=v;
return;
}
pushdown(u);
int mid=l+r>>1;
if(p<=mid)modify2(u<<1,l,mid,p,v);
else modify2(u<<1|1,mid+1,r,p,v);
pushup(u);
}
ll qry1(int u,int l,int r,int p){
if(l==r)return tr[u].val;
pushdown(u);
int mid=l+r>>1;
if(p<=mid)return qry1(u<<1,l,mid,p);
else return qry1(u<<1|1,mid+1,r,p);
}
ll qry2(int u,int l,int r,int L,int R){
if(l>=L&&r<=R)return tr[u].cnt;
pushdown(u);
int mid=l+r>>1;
ll res=0;
if(L<=mid)res+=qry2(u<<1,l,mid,L,R);
if(R>mid)res+=qry2(u<<1|1,mid+1,r,L,R);
return res;
}
}ds;
bool st;
void solve(int cs){
if(!cs)return;
cin>>Q;
n1=n2=1;
id1[n1]=id2[n2]=1;
t1.val[n1]=t2.val[n2]=1;
for(int i=1;i<=Q;i++){
cin>>q[i].t>>q[i].u>>q[i].c;
if(q[i].t==1){
n1++;
id1[n1]=t1.ins(id1[q[i].u],q[i].c);
}
else{
n2++;
id2[n2]=t2.ins(id2[q[i].u],q[i].c);
}
}
dfs1(1,0,0);
dfs2(1,0,0);
sort(lsh+1,lsh+aoao+1);
aoao=unique(lsh+1,lsh+aoao+1)-lsh-1;
for(int i=1;i<=n2;i++){
if(id[id2[i]])continue;
int cur1=0,cur2=0;
int u=id2[i];
for(int j=K-1;~j;j--){
if(!f[j][u])continue;
pii tmp=make_pair(((1ll*cur1*pw1[1<<j])%mod1+g1[j][u])%mod1,((1ll*cur2*pw2[1<<j])%mod2+g2[j][u])%mod2);
auto it=lower_bound(lsh+1,lsh+aoao+1,tmp)-lsh;
if(lsh[it]==tmp){
u=f[j][u];
cur1=tmp.first;
cur2=tmp.second;
}
}
id[id2[i]]=mp[make_pair(cur1,cur2)];
}
for(int i=1;i<=t1.idx;i++){
for(int j=0;j<26;j++){
if(t1.tr[i][j]){
ds.add(i,t1.tr[i][j]);
}
}
}
ds.dfs(1,0);
ds.dfs1(1,0);
ds.dfs2(1,1);
int n=n1;
ds.build(1,1,n);
for(int i=1;i<=n2;i++){
int u=id[id2[i]];
ds.modify2(1,1,n,ds.dfn[u],1);
sum+=ds.qry1(1,1,n,ds.dfn[u]);
}
for(int i=Q;i;i--){
res[i]=sum;
if(q[i].t==1){
sum-=ds.qry2(1,1,n,ds.dfn[id1[n1]],ds.dfn[id1[n1]]+ds.siz[id1[n1]]-1);
ds.modify1(1,1,n,ds.dfn[id1[n1]],ds.dfn[id1[n1]]+ds.siz[id1[n1]]-1,-1);
n1--;
}
else{
int u=id[id2[n2]];
ds.modify2(1,1,n,ds.dfn[u],-1);
sum-=ds.qry1(1,1,n,ds.dfn[u]);
n2--;
}
}
for(int i=1;i<=Q;i++){
cout<<res[i]<<'\n';
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
init();
for(int cs=1;cs<=Tc;cs++){
solve(cs);
}
return 0;
}
01-04
感觉一直在水……
得往前推一推了……
然后 HT 补充了一下 C a t a l a n Catalan Catalan 数,那刷一下吧……
LG2532 [AHOI2012] 树屋阶梯
原题链接:[AHOI2012] 树屋阶梯
分析
看的我一脸懵……
《算法竞赛》说这是所谓的棋盘模型, 我觉得记住结论就行。
如何套用到本题呢?看一眼其实差不多有了一些感觉,同样是 n × n n\times n n×n 的网格,走出一条紧贴对角线的路……
真的一样吗?
呃,我学艺不精了,这里 Catalan \operatorname{Catalan} Catalan 数就是可以用来求这样的紧贴的情形。
那直接整就行,使用这组公式 C n = 4 n − 2 n + 1 × C n − 1 C_n=\frac{4n-2}{n+1}\times C_{n-1} Cn=n+14n−2×Cn−1,但是,需要高精度,我****!
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int BS = 10000;
const int N = 205;
int n;
struct node{
int num[N], len;
void clear(){
memset(num, 0, sizeof(num));
len = 1;
}
void init(int x){
clear();
if (x == 0)
return ;
len = 0;
while (x){
num[len++] = x % BS;
x /= BS;
}
}
int& operator[](int x){
return num[x];
}
const int& operator[](int const &x) const{
return num[x];
}
void print(void){
if (len == 0){
printf("0");
return ;
}
printf("%d", num[len - 1]);
for (int i = len - 2; i >= 0; i--)
printf("%04d", num[i]);
}
};
node operator+(node x, node y){
node res;
res.len = max(x.len, y.len);
for (int i = 0; i <= res.len; i++){
res[i] = 0;
}
int tmp = 0;
for (int i = 0; i < res.len; i++){
int sum = tmp;
if (i < x.len)
sum += x[i];
if (i < y.len)
sum += y[i];
res[i] = sum % BS;
tmp = sum / BS;
}
if (tmp > 0){
res[res.len] = tmp;
res.len++;
}
return res;
}
node operator*(node x, int y){
node res;
res.clear();
if (y == 0){
res.init(0);
return res;
}
int tmp = 0;
for (int i = 0; i < x.len; i++){
long long tp = (long long)x[i] * y + tmp;
res[i] = tp % BS;
tmp = tp / BS;
res.len = i + 1;
}
while (tmp > 0){
res[res.len] = tmp % BS;
tmp /= BS;
res.len++;
}
return res;
}
node operator/(node x, int y){
node res;
res.clear();
if (y == 0){
return res;
}
long long tmp = 0;
for (int i = x.len - 1; i >= 0; i--){
long long cur = tmp * BS + x[i];
res[i] = cur / y;
tmp = cur % y;
if (res[i] > 0 && i >= res.len - 1){
res.len = i + 1;
}
}
while (res.len > 1 && res[res.len - 1] == 0){
res.len--;
}
return res;
}
node ans;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
ans.init(1);
for (int i = 1; i <= n; i++){
node tmp = ans * (4 * i - 2);
ans = tmp / (i + 1);
}
ans.print();
}
两次屈服于高精度都是 AHOI……
可能因为我老家是安徽的吧……
LG1044 [NOIP 2003 普及组] 栈
原题链接:[NOIP 2003 普及组] 栈
bur,我咋写过?本质和括号序列是一样的,那还说啥?
LG1641 [SCOI2010] 生成字符串
原题链接:[SCOI2010] 生成字符串
分析
先筛一发质数,20100403 显然是质数。
真的吗?转化为括号序列问题,设 1 1 1 为"(“, 0 0 0 为”)",那么,要求序列的那个栈始终不为空,那爽了!
那上一个 Catalan \operatorname{Catalan} Catalan 数套一个 L u c a s Lucas Lucas 定理?可能需要吧……
1 e 6 1e6 1e6 的话,用 C n = 1 n + 1 ( 2 n n ) C_n=\frac{1}{n+1}\binom{2n}{n} Cn=n+11(n2n) 可能比较划算吧……
啊?真假的?那我在场上咋能想到呢?算了,先敲代码。
坏了,想简单了/ll
我们的括号序列只给一个长度,其他的不给啊!这个直接限制了 0/1 的个数。
那咋写?插板?难道说,我先把 0 / 1 0/1 0/1 穿插排一下,然后,剩下的 1 1 1 插板?
那也不对,我可以一堆 1 1 1 接一堆 0 0 0。
那不炸了?还是式子的问题,否则稳赢的。
这么牛!抽象成 网格!为什么这么想呢?本质还是因为有 0 / 1 0/1 0/1 两种考虑, 那把他当成向上和向右走,那么,就是向右走的不超过向上走的,即在对角线及以上?
这才是真正的
Catalan
\operatorname{Catalan}
Catalan 数变种!那么
a
n
s
=
(
n
+
m
n
−
1
)
ans=\binom{n+m}{n-1}
ans=(n−1n+m)
吗?
别问我啊!手模一发啊?不对……
难道,我把这个东西先当成一个正方形,再把向上的插回去?
那么,式子还是错的啊/ll
看题解吧,有点小崩溃/ll
行吧,搬了个题解的式子
a
n
s
=
(
n
+
m
m
)
−
(
n
+
m
m
−
1
)
ans=\binom{n+m}{m}-\binom{n+m}{m-1}
ans=(mn+m)−(m−1n+m)
这是啥啊!
正解
#include <bits/stdc++.h>
#define mod 20100403
#define int long long
using namespace std;
const int N = 2000005;
int fac[N];
int n, m;
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int C(int n, int m){
if (m > n)
return 0;
return fac[n] * qpow(fac[m], mod - 2) % mod * qpow(fac[n - m], mod - 2) % mod;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
fac[0] = 1;
for (int i = 1; i < N; i++){
fac[i] = fac[i - 1] * i % mod;
}
cout << (C(n + m, m) - C(n + m, m - 1) + mod) % mod;
}
过的稀里糊涂……
LG1722 矩阵 II
原题链接:矩阵 II
分析
咋推出来 2 的?哦,“注意红色算筹和黑色算筹的数量必须相等”。
坏了,切不了橙了。
不不不,这是个括号序列!烫烫烫!还是卡特兰数!傻了傻了!
模数是100?扩欧求逆元?
这题拿 Catalan 写的是 gay,我再也不干这事了。
就一个递推,我****!
正解
#include <bits/stdc++.h>
#define mod 100
using namespace std;
const int N = 205;
int n, c[N][N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
c[1][1] = 1;
for (int i = 2; i <= (n << 1); i++){
for (int j = (i + 1) >> 1; j <= i; j++){
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
cout << c[n + n][n];
}
839

被折叠的 条评论
为什么被折叠?



