2026-01-02~01-04 hetao1733837 的刷题笔记

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+14n2×Cn1,但是,需要高精度,我****!

正解

#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=(n1n+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)(m1n+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];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值