CF438E-生成函数

CF438E

题目描述

题目描述

题解

首先考虑dp,定义f[i]f[i]f[i]表示权值为iii的二叉树有多少种方案?
f[i]=∑j=1n∑k=0i−c[j]f[k]f[i−k−c[j]] f[i]=\sum\limits_{j=1}^{n}\sum\limits_{k=0}^{i-c[j]}f[k]f[i-k-c[j]] f[i]=j=1nk=0ic[j]f[k]f[ikc[j]]
表示根节点权值为c[j]c[j]c[j],左子树权值为kkk,右子树权值为i−k−c[j]i-k-c[j]ikc[j]的树的方案数,累加起来即可。
时间复杂度为O(n3)O(n^3)O(n3)

考虑优化。
我们观察这个式子,感觉像是一个卷积的形式,但是不是,我们考虑将其转化为三重卷积的形式。
那么需要一个g[c[j]]g[c[j]]g[c[j]],显然g[]g[]g[]只会在c[j]c[j]c[j]处有值。

于是式子就转化成了:
f[i]=∑j=1n∑k=0i−c[j]f[k]f[i−k−c[j]]g[c[j]],[n>0]f[i]=∑j=0i∑k=0i−jf[k]f[i−k−j]g[j],[n>0] f[i]=\sum\limits_{j=1}^{n}\sum\limits_{k=0}^{i-c[j]}f[k]f[i-k-c[j]]g[c[j]],[n>0]\\ f[i]=\sum\limits_{j=0}^{i}\sum\limits_{k=0}^{i-j}f[k]f[i-k-j]g[j],[n>0]\\ f[i]=j=1nk=0ic[j]f[k]f[ikc[j]]g[c[j]][n>0]f[i]=j=0ik=0ijf[k]f[ikj]g[j],[n>0]

我们写出f[i],g[i]f[i],g[i]f[i],g[i]的生成函数:
F(x)=∑i=0∞f[i]xiG(x)=∑i=0∞g[i]xi F(x)=\sum\limits_{i=0}^{\infty}f[i]x^i\\ G(x)=\sum\limits_{i=0}^{\infty}g[i]x^i\\ F(x)=i=0f[i]xiG(x)=i=0g[i]xi
那么
f[i]=∑j=0∞∑k=0∞f[k]f[i−k−j]g[j],[n>0]F(x)=F(x)2G(x)+1 f[i]=\sum\limits_{j=0}^{\infty}\sum\limits_{k=0}^{\infty}f[k]f[i-k-j]g[j],[n>0]\\ F(x)=F(x)^2G(x)+1\\ f[i]=j=0k=0f[k]f[ikj]g[j],[n>0]F(x)=F(x)2G(x)+1
111是因为f[0]=1f[0]=1f[0]=1,而g[0]=0g[0]=0g[0]=0,乘上去的话,常数项就为000了,所以还要再加上111.
那么求根公式接一元二次方程可得:
F(x)=−1±1−4G(x)2G(x) F(x)=\frac{-1\pm\sqrt{1-4G(x)}}{2G(x)}\\ F(x)=2G(x)1±14G(x)
分子有理化:
F(x)=21±1−4G(x) F(x)=\frac{2}{1\pm\sqrt{1-4G(x)}}\\ F(x)=1±14G(x)2
考虑取舍问题,假如取负号,将000带入原式,发现F(0)=f[0]=∞F(0)=f[0]=\inftyF(0)=f[0]=,不符合题意,故舍去。
那么最终答案就是:
F(x)=21+1−4G(x) F(x)=\frac{2}{1+\sqrt{1-4G(x)}}\\ F(x)=1+14G(x)2
多项式开根,多项式求逆即可解决,时间复杂度O(nlogn)O(nlogn)O(nlogn)

代码

暴力O(n3)O(n^3)O(n3)

#include<bits/stdc++.h>
#define int long long
#define M 1000009
using namespace std;
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
int n,m,f[M],c[M];
const int mod=998244353;
signed main(){
	n=read(),m=read();f[0]=1;
	for(int i=1;i<=n;i++) c[i]=read();
	for(int i=1;i<=m;i++)
		for(int j=0;j<=i;j++)
			for(int k=1;k<=n;k++)
				if(i>=j+c[k])f[i]=(f[i]+f[j]*f[i-c[k]-j]%mod)%mod;
	for(int i=1;i<=m;i++) printf("%lld\n",f[i]);
	return 0;
} 

正解O(nlogn)O(nlogn)O(nlogn)

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int g=3;
const int mod=998244353; 
const int M=400009;
char s;
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
int ksm(int a,int b){//快速幂 
	int ans=1;
	while(b){
		if(b&1) ans=(ll)ans*a%mod;
		a=(ll)a*a%mod;
		b>>=1;
	}return ans%mod;
}
int c[M],d[M],n,m,r[M],tmp[M],a[M],b[M],inv2=ksm(2,mod-2),dera[M],inva[M],lnb[M],lna[M],k,aa[M],bb[M],aaa[M],bbb[M],invb[M],F[M],cpy[M];
void ntt(int *A,int lim,int type){//ntt
	for(int i=0;i<lim;i++) if(i<r[i]) swap(A[i],A[r[i]]);
	for(int mid=1;mid<lim;mid<<=1){
		int W=ksm(g,(mod-1)/(mid<<1));
		for(int R=mid<<1,j=0;j<lim;j+=R){
			int w=1;
			for(ll k=0;k<mid;k++,w=(ll)w*W%mod){
				int x=A[j+k],y=(ll)w*A[j+k+mid]%mod;
				A[j+k]=(x+y)%mod;
				A[j+mid+k]=(x-y+mod)%mod;
			}
		}
	}
	if(type==-1){
		reverse(A+1,A+lim);
        int inv=ksm(lim,mod-2);
        for(int i=0;i<lim;i++) A[i]=(ll)A[i]*inv%mod;
	}
}
void getmul(int a[],int b[],int c[],int n,int m){//多项式乘法 
	int lim=1,l=0;
    while(lim<=n+m) lim<<=1,l++;
    for(int i=1;i<lim;i++) r[i]=(r[i>>1]>>1|(i&1)<<(l-1));
    for(int i=0;i<=n;i++) aa[i]=a[i];
    for(int i=0;i<=m;i++) bb[i]=b[i];
    ntt(aa,lim,1),ntt(bb,lim,1);
    for(int i=0;i<lim;i++){
        c[i]=(ll)aa[i]*bb[i]%mod;
        aa[i]=bb[i]=0;
    }ntt(c,lim,-1);
}
void getinv(int a[],int b[],int len){//多项式求逆 
	if(len==1){
		b[0]=ksm(a[0],mod-2);
		return;
	}getinv(a,b,(len+1)>>1);
	int lim=1,l=0;
	while(lim<len+len) lim<<=1,l++;
	for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;i<len;i++) tmp[i]=a[i];
    for(int i=len;i<lim;i++) tmp[i]=0;
	ntt(tmp,lim,1),ntt(b,lim,1);
	for(int i=0;i<lim;i++) b[i]=(ll)b[i]*((2-(ll)tmp[i]*b[i]%mod+mod)%mod)%mod;
	ntt(b,lim,-1);
	for(int i=len;i<lim;i++) b[i]=0;
}
void getdiv(int a[],int b[],int c[],int d[],int n,int m){//多项式除法 
	for(int i=0;i<=n;i++) aaa[i]=a[i];
	for(int i=0;i<=m;i++) bbb[i]=b[i];
	reverse(aaa,aaa+n+1),reverse(bbb,bbb+m+1);
	getinv(bbb,invb,n-m+1);
	getmul(aaa,invb,c,n-m,n-m);
	reverse(c,c+n-m+1);
    reverse(aaa,aaa+n+1);reverse(bbb,bbb+m+1);
    getmul(c,bbb,d,n-m,m);
    for(int i=0;i<m;i++) d[i]=(aaa[i]-d[i]+mod)%mod;
    for(int i=0;i<=n;i++) aaa[i]=0;
	for(int i=0;i<=m;i++) bbb[i]=0;
}
void getsqrt(int a[],int b[],int len){//多项式开根 
	if(len==1) return (void)(b[0]=1);
	getsqrt(a,b,(len+1)>>1);
	for(int i=0;i<=(len<<1);i++) F[i]=0;
	getinv(b,F,len);
	int lim=1,l=0;
	while(lim<len+len) lim<<=1,l++;
	for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;i<len;i++) cpy[i]=a[i];
    for(int i=len;i<lim;i++) cpy[i]=0;
	ntt(cpy,lim,1),ntt(b,lim,1),ntt(F,lim,1);
	for(int i=0;i<lim;i++) b[i]=((ll)((ll)(b[i]+(ll)cpy[i]*F[i]%mod)%mod)*inv2)%mod;
	ntt(b,lim,-1);
	for(int i=len;i<lim;i++) b[i]=0;
}
void getintegral(int a[],int b[],int len){//积分 
	for(int i=1;i<len;i++) b[i]=(ll)a[i-1]*ksm(i,mod-2)%mod;
	b[0]=0;
}
void getderivation(int a[],int b[],int len){//求导 
	for(int i=1;i<len;i++) b[i-1]=(ll)a[i]*i%mod;
	b[len-1]=0; 
}
void getln(int a[],int b[],int len){//多项式ln 
	getinv(a,inva,len);
	getderivation(a,dera,len);
	int lim=1,l=0;
	while(lim<len+len) lim<<=1,l++;
	for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	ntt(inva,lim,1),ntt(dera,lim,1);
	for(int i=0;i<lim;i++) inva[i]=(ll)inva[i]*dera[i]%mod;
	ntt(inva,lim,-1);
	getintegral(inva,b,len);
	for(int i=0;i<lim;i++) inva[i]=dera[i]=0;
}
void getexp(int a[],int b[],int len){//多项式exp 
	if(len==1) return (void)(b[0]=1);
	getexp(a,b,len>>1),getln(b,lnb,len);
	for(int i=0;i<len;i++) lnb[i]=(ll)(a[i]-lnb[i]+(i==0)+mod)%mod; 
	int lim=(len<<1);
	ntt(lnb,lim,1),ntt(b,lim,1);
	for(int i=0;i<lim;i++) b[i]=(ll)lnb[i]*b[i]%mod;
	ntt(b,lim,-1);
	for(int i=len;i<lim;i++) lnb[i]=b[i]=0;
}
void getksm(int a[],int b[],int len,int k){//多项式快速幂 
	getln(a,lna,len);
	for(int i=0;i<len;i++) lna[i]=(ll)k*lna[i]%mod;
	getexp(lna,b,len);
}
signed main(){
	n=read(),m=read();a[0]=1;
	for(int i=1;i<=n;i++) a[read()]=-4;
	int lim=1;while(lim<=m) lim<<=1;
	getsqrt(a,b,lim);b[0]=(ll)(b[0]+1)%mod;
	getinv(b,c,lim);
	for(int i=1;i<=m;i++) printf("%d\n",(ll)c[i]*2%mod);
	return 0;
}
你遇到的错误: > **`OleCreatePictureIndirect` 返回值:`-2147467259`(即 `0x80004005`)** 这个是经典的 COM 错误,对应的含义是: --- ## ❌ 错误代码解析 ``` Error: -2147467259 (0x80004005) Name: Unspecified error / E_FAIL Meaning: 未指定的失败 —— 通常是权限、资源或系统限制导致 ``` 这是在使用 `OleCreatePictureIndirect` 创建图片对象时最常见的问题之一。 --- ## 🔍 常见原因分析(针对你的场景) | 原因 | 说明 | |------|------| | ✅ **剪贴板图像不是 DIB 位图(DIB = Device Independent Bitmap)** | Windows 截图工具有时会以 `CF_DIB` 格式写入剪贴板,而不是 `CF_BITMAP`。而 `GetClipboardData(CF_BITMAP)` 只能获取 GDI 位图句柄,某些情况下为 `0` | | ⚠️ **句柄已释放或跨线程访问受限** | 系统截图后,剪贴板中的 GDI 句柄可能被延迟创建或已被释放 | | 💣 **Office 安全策略阻止 OLE 图像构造** | 某些企业环境禁用了低级 OLE 操作 | | 🖼️ **图片过大或内存不足** | 高分辨率截图可能导致分配失败 | | 🧩 **UUID 结构体字节顺序错误** | 虽然我们设置了 IID,但字节填充不对可能导致接口不匹配 | --- ## ✅ 终极解决方案:改用 `CF_DIB` + 手动构造位图 我们要放弃直接读取 `CF_BITMAP` 的方式,转而使用更稳定、跨平台兼容性更好的: > ✅ **读取 `CF_DIB` 格式数据 → 构造 DIBSECTION → 再通过 `OleCreatePictureIndirect` 创建图片** 这种方式适用于: - Win10 / Win11 - 32/64 位 Office - 微信/QQ/浏览器截图等所有主流来源 --- ## ✅ 修正版 `PasteClipboardImage` 函数(支持 `CF_DIB`,彻底解决 `-2147467259`) ```vba Public Sub PasteClipboardImage(ws As Worksheet, col As Long, row As Long, colWidthChars As Double, rowHeightPts As Double) On Error Resume Next Dim hMem As LongPtr, pDIB As LongPtr Dim uPicInfo As uPicDesc Dim iidIPicture As UUID Dim Pic As IPicture Dim success As Long Dim startTime As Double Dim timeoutSeconds As Integer: timeoutSeconds = 20 Dim fso As Object Set fso = CreateObject("Scripting.FileSystemObject") Application.StatusBar = "⏳ 正在等待剪贴板图像,请使用【Win + Shift + S】截图..." ' --- 等待剪贴板包含 CF_DIB 数据 --- DoEvents startTime = Timer Do While Timer < startTime + timeoutSeconds DoEvents Application.Wait Now + TimeValue("00:00:00.5") If OpenClipboard(0&) <> 0 Then ' 尝试获取 CF_DIB(设备无关位图) hMem = GetClipboardData(&H8) ' &H8 = CF_DIB CloseClipboard If hMem <> 0 Then Exit Do End If Loop If hMem = 0 Then MsgBox "❌ 超时:剪贴板中没有图像数据(CF_DIB 格式)" & vbCrLf & _ "请确保:" & vbCrLf & _ "1. 使用了【Win + Shift + S】成功截图" & vbCrLf & _ "2. 没有其他程序清空剪贴板", vbExclamation Application.StatusBar = "等待截图超时" Exit Sub End If ' --- 获取内存指针 --- #If VBA7 Then pDIB = SafeMemoryHandle(hMem) #Else pDIB = hMem ' 32位下直接使用 #End If If pDIB = 0 Then MsgBox "无法访问剪贴板内存块。", vbCritical Exit Sub End If ' --- 初始化 Picture 描述结构 --- With uPicInfo .Size = LenB(uPicInfo) .Type = 1 ' PICTYPE_BITMAP .hPic = hMem ' 注意:传的是 Global Memory Handle,不是指针! .hPal = 0 End With ' --- 设置 IPicture 的 IID {7BF80980-BF32-101A-8FFF-00AA00300CAB} --- With iidIPicture .Data1 = &H7BF80980 .Data2 = &HBF32 .Data3 = &H101A For i = 0 To 7 Select Case i Case 0: .Data4(i) = &H8F Case 1: .Data4(i) = &HF Case 2: .Data4(i) = &H0 Case 3: .Data4(i) = &HA Case 4: .Data4(i) = &HA Case 5: .Data4(i) = &H0 Case 6: .Data4(i) = &H30 Case 7: .Data4(i) = &HC End Select Next i End With ' --- 创建 IPicture 对象 --- Err.Clear success = OleCreatePictureIndirect(uPicInfo, iidIPicture, 1, Pic) If success <> 0 Or Err.Number <> 0 Or Pic Is Nothing Then MsgBox "❌ OleCreatePictureIndirect 失败!" & vbCrLf & _ "返回码: " & success & " (" & Hex(success) & ")" & vbCrLf & _ "可能原因:" & vbCrLf & _ "- 剪贴板格式不兼容" & vbCrLf & _ "- Office 安全设置过高" & vbCrLf & _ "- 内存不足或系统资源紧张", vbCritical Debug.Print "OleCreatePictureIndirect failed with code: " & success Exit Sub End If ' --- 插入图片到 Excel --- Dim picObj As Picture Set picObj = ws.Pictures.Insert(Pic) If Not picObj Is Nothing Then Call ResizePictureToFitCell(picObj, ws.Cells(row, col), colWidthChars, rowHeightPts) Application.StatusBar = "✅ 成功插入截图" Else MsgBox "⚠️ 图片对象创建成功,但未能插入工作表。", vbExclamation End If Application.Wait Now + TimeValue("00:00:00.5") End Sub ``` --- ## ✅ 补充函数:安全获取内存句柄(仅用于 VBA7/64位) ```vba #If VBA7 Then Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As Long #End If ' 安全锁定内存块(用于调试用途) Public Function SafeMemoryHandle(hMem As LongPtr) As LongPtr On Error Resume Next #If VBA7 Then SafeMemoryHandle = GlobalLock(hMem) If SafeMemoryHandle <> 0 Then GlobalUnlock hMem ' 立即解锁(我们只验证可访问) End If #Else SafeMemoryHandle = hMem #End If End Function ``` --- ## ✅ 修改建议汇总 ### ✅ 必须修改的地方: | 原操作 | 改为 | |-------|------| | `GetClipboardData(CF_BITMAP)` | ➜ `GetClipboardData(&H8)` 即 `CF_DIB` | | `.hPic = hBitmap`(GDI 句柄) | ➜ `.hPic = hGlobalMemory`(内存块句柄) | | 使用简单整数数组传 IID | ➜ 使用完整 `UUID` 类型结构体 | --- ## ✅ 为什么 `CF_DIB` 更可靠? | 特性 | `CF_BITMAP` | `CF_DIB` | |------|------------|---------| | 是否跨进程安全 | ❌ 否(GDI 句柄仅当前进程有效) | ✅ 是(内存块可复制) | | 是否被现代应用支持 | ⚠️ 部分支持 | ✅ 广泛支持(微信、Edge、Snip & Sketch) | | 是否可在不同线程使用 | ❌ 否 | ✅ 是 | | 兼容性 | 差 | 好 | 👉 所以你应该优先使用 `CF_DIB`! --- ## ✅ 测试方法 你可以添加一个测试宏来检查剪贴板内容: ```vba Sub TestClipboardFormats() If OpenClipboard(0&) Then Dim fmt As Long fmt = 0 Do fmt = EnumClipboardFormats(fmt) If fmt = 0 Then Exit Do Debug.Print "Clipboard Format Code: " & fmt Select Case fmt Case 2: Debug.Print " --> CF_BITMAP" Case 8: Debug.Print " --> CF_DIB" End Select Loop CloseClipboard End If End Sub ``` 运行它看看是否有 `8`(即 `CF_DIB`)。 --- ## ✅ 总结:你现在应该怎么做? ✅ **替换原来的 `PasteClipboardImage` 函数为上面这个支持 `CF_DIB` 的版本** ✅ **保留 `LaunchSnippingTool` 不变** ✅ **确保用户使用【Win + Shift + S】截图(生成标准 DIB)** ✅ **避免使用第三方软件干扰剪贴板** --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值