Problem B. Harvest of Apples HDU - 6333(莫队算法+组合数)

本文针对HDU-6333问题,介绍了一种利用组合数性质和莫队算法优化查询的方法,通过预处理阶乘和逆元实现O(1)时间计算,适用于大量数据查询。

Problem B. Harvest of Apples HDU - 6333

There are n apples on a tree, numbered from 1 to n.
Count the number of ways to pick at most m
apples.
Input
The first line of the input contains an integer T (1≤T≤105) denoting the number of test cases.
Each test case consists of one line with two integers n,m (1≤m≤n≤105)
.
Output
For each test case, print an integer representing the number of ways modulo 109+7
.
Sample Input

2
5 2
1000 500

Sample Output

16
924129523
题意:

计算C(n,0)到C(n,m)的和,T(T<=1e5)组数据。

分析:

首先根据求组合数的公式

Cmn=n!m!(nm)! C n m = n ! m ! ( n − m ) !

所以首先我们可以预处理出阶乘和逆元,复杂度为O(N)

如果我们每次累加那么必然会超时

根据组合数的性质我们定义

Smn=mi=0Cin S n m = ∑ i = 0 m C n i

所以比较显然的两个公式有:

Sm1n=SmnCmn S n m − 1 = S n m − C n m

Sm+1n=Smn+Cm+1n S n m + 1 = S n m + C n m + 1 (或者 Smn=Sm1n+Cmn S n m = S n m − 1 + C n m )

根据性质

Ckn=Ckn1+Ck1n1 C n k = C n − 1 k + C n − 1 k − 1

我们有公式:

Smn+1=2SmnCmn S n + 1 m = 2 S n m − C n m

Smn1=Smn+Cmn12 S n − 1 m = S n m + C n − 1 m 2 (或者 Smn=Smn+1+Cmn2 S n m = S n + 1 m + C n m 2 )

如下图:
这里写图片描述

其中的 C0n1C0n C n − 1 0 可 以 充 当 C n 0 因为都是1嘛

然后再减去 Cmn1 C n − 1 m 即可


这样我们得到了四个公式

Sm1n=SmnCmn S n m − 1 = S n m − C n m

Sm+1n=Smn+Cm+1n S n m + 1 = S n m + C n m + 1 (或者 Smn=Sm1n+Cmn S n m = S n m − 1 + C n m )

Smn+1=2SmnCmn S n + 1 m = 2 S n m − C n m

Smn1=Smn+Cmn12 S n − 1 m = S n m + C n − 1 m 2 (或者 Smn=Smn+1+Cmn2 S n m = S n + 1 m + C n m 2 )

可以把它想象成区间查询问题

这样我们可以在O(1)时间内计算出 Sm1n,Sm+1n,Smn+1,Smn1 S n m − 1 , S n m + 1 , S n + 1 m , S n − 1 m

因此可以使用莫队算法离线处理T组查询。

因为第一次写莫队,这篇博客讲的非常详细

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+7;
ll fac[maxn],inv[maxn];
ll rev2;
struct Query{
    int L,R,id,block;
    bool operator < (const Query &p)const{//按照分块排序,再按照右端点排序
        if(block == p.block) return R < p.R;
        return block < p.block;
    }
}Q[maxn];
ll res;
ll ans[maxn];

ll q_pow(ll a,ll b){
    ll ans = 1;
    while(b){
        if(b & 1)
            ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}//快速幂取模

ll C(int n,int k){
    return fac[n] * inv[k] % mod * inv[n-k] % mod;
}//组合数公式

void init(){
    rev2 = q_pow(2,mod-2);
    fac[0] = fac[1] = 1;
    for(int i = 2; i < maxn; i++){
        fac[i] = i * fac[i-1] % mod;
    }//预处理阶乘
    inv[maxn-1] = q_pow(fac[maxn-1],mod-2);
    for(int i = maxn-2; i >= 0; i--){
        inv[i] = inv[i+1] * (i + 1) % mod;
    }//预处理阶乘的逆元(很巧妙,不需要每次求逆元了)
}

inline void addN(int posL,int posR){//因为传进来的posL已经加1了,所以求S(posL,posR)=2S(posL-1,posR)-C(posL-1,posR)
                                    //而S(posL-1,posR)就是上一次的结果res,故只需要算C(posL-1,posR)
    res = (2 * res % mod - C(posL-1,posR) + mod) % mod;
}

inline void addM(int posL,int posR){//因为传进来的posR已经自增完成,res是上一次的结果S(posL,posR-1)故只需要求C(posL,posR)
    res = (res + C(posL,posR)) % mod;
}

inline void delN(int posL,int posR){//因为传进来的是后缀自增,所以posL还是原来的值
                                    //那么新的S(posL-1,posR)=(S(posL,posR)+C(posL-1,posR))/2,其中S(posL,posR)就是res
    res = (res + C(posL-1,posR)) % mod * rev2 % mod;
}

inline void delM(int posL,int posR){//因为传进来的是后缀自增,所以posR还是原来的值
                                    //那么新的S(posL,posR-1)=S(posL,posR)-C(posL,posR),其中S(posL,posR)就是res
    res = (res - C(posL,posR) + mod) % mod;
}

int main(){
    int T;
    init();
    int len = (int)sqrt(maxn*1.0);
    scanf("%d",&T);
    for(int i = 1; i <= T; i++){
        scanf("%d%d",&Q[i].L,&Q[i].R);
        Q[i].id = i;//记录下查询顺序编号
        Q[i].block = Q[i].L / len;//块号
    }
    sort(Q+1,Q+1+T);//排序
    res = 2;
    int curL = 1,curR = 1;
    for(int i = 1; i <= T; i++){
        while(curL < Q[i].L) addN(++curL,curR);//需要算S(curL+1,curR)=2S(curL,curR)-C(curL,curR)
        while(curR < Q[i].R) addM(curL,++curR);//需要算S(curL,curR+1)=S(curL,curR)+C(curL,curR+1)
        while(curL > Q[i].L) delN(curL--,curR);//需要算S(curL-1,curR)=(S(curL,curR)+C(curL-1,curT))/2
        while(curR > Q[i].R) delM(curL,curR--);//需要算S(curL,curR-1)=S(curL,curR)-C(curL,curR)
        ans[Q[i].id] = res;
    }
    for(int i = 1; i <= T; i++){
        printf("%lld\n",ans[i]);
    }
    return 0;
}
<think>我们正在处理如何将指定目录下的文件打包到UsbDK的安装程序(MSI和EXE)中。根据之前的知识,UsbDk使用WiX Toolset来创建安装包。因此,我们需要修改WiX脚本(.wxs文件)来包含这些文件。 步骤如下: 1. **定位WiX脚本**:通常,UsbDk的源代码中会包含一个或多个.wxs文件(例如`Install\UsbDk.wxs`)。我们需要编辑这个文件。 2. **添加文件到WiX脚本**:在WiX脚本中,我们需要为每个文件添加`<Component>`和`<File>`元素,并将它们分配到适当的目录中。同时,还要考虑不同平台(x86/x64)和操作系统版本。 3. **处理特殊文件**: - **驱动程序文件(.sys和.inf)**:这些需要特殊的处理,比如使用`Driver`元素来安装。 - **DLL和EXE文件**:这些可以直接复制到目标目录。 - **include文件夹**:这通常用于开发,所以可能需要安装到程序文件的包含目录下。 4. **目录结构**: - 驱动程序通常安装到`System32\drivers`。 - 可执行文件和DLL通常安装到程序目录(例如`ProgramFilesFolder\UsbDk`)。 - include文件夹可以安装到程序目录下的`include`子目录。 5. **构建安装包**:使用WiX工具集(如candle.exe和light.exe)编译和链接WiX脚本,生成MSI文件。然后,可以使用WiX的bundle项目(.wxs)将MSI打包成EXE安装程序(如果需要)。 具体操作: 假设我们的源文件在:`E:\3.windows_code\4.UsbDk\UsbDk-1.00-22\Install_Debug\x64\Win10Debug\UsbDk_Package` 包含: - include文件夹 - UsbDk.inf - UsbDk.sys - UsbDkController.exe - UsbDkHelper.dll - UsbDkInstHelper.exe - WdfCoinstaller01011.dll 我们以64位系统为例: ### 步骤1:在WiX脚本中定义目录结构 我们需要在WiX脚本中定义安装目录。例如: ```xml <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFiles64Folder"> <Directory Id="UsbDkInstallDir" Name="UsbDk"> <!-- 这里放置可执行文件、DLL和include文件夹 --> <Directory Id="IncludeDir" Name="include" /> </Directory> </Directory> <Directory Id="System64Folder"> <Directory Id="DriverDir" Name="drivers" /> </Directory> </Directory> ``` ### 步骤2:添加文件到组件 对于每个文件,我们创建一个组件,并将其放入对应的目录。 **示例:安装UsbDkHelper.dll到程序目录** ```xml <Component Id="UsbDkHelperDLL" Guid="*"> <File Id="UsbDkHelperDLL" Source="E:\3.windows_code\4.UsbDk\UsbDk-1.00-22\Install_Debug\x64\Win10Debug\UsbDk_Package\UsbDkHelper.dll" KeyPath="yes" /> </Component> ``` **示例:安装驱动程序** 驱动程序安装需要特殊处理。我们需要使用`Driver`元素,并且通常需要放在`System32\drivers`目录下。同时,还需要注册驱动服务。 ```xml <Component Id="UsbDkDriver" Guid="*"> <File Id="UsbDkSys" Source="E:\3.windows_code\4.UsbDk\UsbDk-1.00-22\Install_Debug\x64\Win10Debug\UsbDk_Package\UsbDk.sys" KeyPath="yes" /> <File Id="UsbDkInf" Source="E:\3.windows_code\4.UsbDk\UsbDk-1.00-22\Install_Debug\x64\Win10Debug\UsbDk_Package\UsbDk.inf" /> <!-- 使用Driver元素注册驱动 --> <Driver AddRemovePrograms="no" DeleteFiles="yes" Legacy="yes" PlugAndPlayPrompt="no" Sequence="1" /> </Component> ``` **注意**:驱动安装可能需要更复杂的操作,例如根据操作系统版本选择不同的驱动文件(Win7/Win8/Win10可能有差异)。这里简化了。 **示例:安装include文件夹** 我们可以使用`<Component>`和`<File>`元素递归包含整个目录。 ```xml <Component Id="IncludeFiles" Guid="*"> <!-- 使用Heat工具预生成目录结构会更方便 --> <!-- 这里手动添加一个示例 --> <File Id="IncludeFile1" Source="E:\3.windows_code\4.UsbDk\UsbDk-1.00-22\Install_Debug\x64\Win10Debug\UsbDk_Package\include\UsbDk.h" /> <!-- 更多文件... --> </Component> ``` 但是,手动添加整个include文件夹的文件很繁琐,我们可以使用WiX的Heat工具(Harvest Tool)来生成这部分代码。 ### 步骤3:使用Heat工具收集文件 在WiX项目中使用Heat工具自动生成目录的组件。 例如,收集include文件夹: ```bash heat.exe dir "E:\3.windows_code\4.UsbDk\UsbDk-1.00-22\Install_Debug\x64\Win10Debug\UsbDk_Package\include" -o Include.wxs -gg -sfrag -srd -dr IncludeDir ``` 这将生成一个`Include.wxs`文件,其中包含了`include`目录下所有文件的组件。然后,我们在主wxs文件中包含这个wxs文件。 ### 步骤4:将组件分组到特性(Feature)中 在WiX中,组件必须属于一个特性。 ```xml <Feature Id="MainFeature" Title="UsbDk" Level="1"> <ComponentRef Id="UsbDkHelperDLL" /> <ComponentRef Id="UsbDkDriver" /> <ComponentRef Id="IncludeFiles" /> <!-- 其他组件引用 --> </Feature> ``` ### 步骤5:编译和链接 使用candle.exe编译wxs文件,然后使用light.exe链接。 ### 步骤6:创建EXE安装包(可选) 如果需要EXE安装包,我们可以创建一个bundle项目(使用Burn)。在bundle的wxs文件中,将生成的MSI作为包包含进去。 ```xml <Bundle ...> <BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense" /> <Chain> <MsiPackage SourceFile="path\to\your\UsbDk.msi" /> </Chain> </Bundle> ``` 然后使用candle和light编译链接这个bundle项目,生成EXE文件。 ### 注意事项 1. **路径问题**:在WiX脚本中,源文件路径可以使用绝对路径,但为了可移植性,通常将文件放在WiX项目目录中,然后使用相对路径。 2. **平台差异**:需要根据平台(x86/x64)分别处理,可以通过条件语句(如`Condition`)或不同的组件组来实现。 3. **驱动签名**:驱动程序需要签名,否则在64位系统上无法安装。确保在打包前已经签名。 以上是一个大致的步骤。具体操作需要根据UsbDk项目的实际情况调整WiX脚本。 §§相关问题§§ 1. 如何使用WiX Toolset创建驱动程序安装包? 2. 在WiX中如何处理不同Windows版本(如Win7/Win10)的驱动程序安装? 3. 如何对安装包进行数字签名? 4. 如何使用WiX的Heat工具自动收集文件? 5. 如何在WiX中创建卸载快捷方式? </think>### 如何将指定产物打包到UsbDK的安装程序 要将指定目录下的产物打包到UsbDK的MSI和EXE安装程序中,需要修改WiX安装脚本(`.wxs`文件)并重建安装包。以下是详细步骤: #### 1. **定位WiX脚本文件** 在UsbDk源码中找到WiX安装脚本(通常为`UsbDk.wxs`或类似名称),这是控制安装包内容的核心文件: ```xml <!-- 示例路径 --> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Product Id="*" Name="UsbDk" Language="1033" Version="1.0.0" Manufacturer="..." UpgradeCode="..."> <Package InstallerVersion="200" Compressed="yes"/> <!-- 主要组件定义在这里 --> </Product> </Wix> ``` #### 2. **添加文件到WiX脚本** 在`<Product>`节点下添加以下组件定义,将您的产物文件包含进安装包: ```xml <!-- 添加驱动程序文件 --> <Component Id="DriverFiles" Guid="YOUR-GUID-HERE"> <File Id="UsbDkSys" Source="E:\3.windows_code\...\UsbDk.sys" KeyPath="yes"/> <File Id="UsbDkInf" Source="E:\3.windows_code\...\UsbDk.inf"/> </Component> <!-- 添加可执行文件和DLL --> <Component Id="BinFiles" Guid="YOUR-GUID-HERE"> <File Id="UsbDkController" Source="E:\3.windows_code\...\UsbDkController.exe"/> <File Id="UsbDkHelperDll" Source="E:\3.windows_code\...\UsbDkHelper.dll"/> <File Id="UsbDkInstHelper" Source="E:\3.windows_code\...\UsbDkInstHelper.exe"/> <File Id="WdfCoinstaller" Source="E:\3.windows_code\...\WdfCoinstaller01011.dll"/> </Component> <!-- 添加include文件夹 --> <DirectoryRef Id="INSTALLFOLDER"> <Component Id="IncludeFiles" Guid="YOUR-GUID-HERE"> <CreateFolder/> <File Source="E:\3.windows_code\...\include\UsbDk.h"/> <!-- 添加具体头文件 --> <!-- 重复添加所有头文件 --> </Component> </DirectoryRef> ``` **注意**: - 每个`Component`需要唯一`Guid`(可用`*`自动生成) - `KeyPath="yes"`标记主文件 #### 3. **配置安装目录结构** 定义文件安装路径: ```xml <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder"> <Directory Id="INSTALLFOLDER" Name="UsbDk"> <Directory Id="IncludeDir" Name="include"/> <!-- include目录 --> </Directory> </Directory> <Directory Id="System64Folder"> <!-- 系统目录 --> <Directory Id="DriverDir" Name="drivers"/> <!-- 驱动安装位置 --> </Directory> </Directory> ``` #### 4. **创建构建脚本** 使用WiX工具集编译安装包(命令行示例): ```powershell # 编译WiX脚本 candle.exe UsbDk.wxs -out obj\ # 链接生成MSI light.exe obj\Usbk.wixobj -out UsbDk_Setup.msi # 创建EXE安装包(使用Burn) heat.exe dir "E:\3.windows_code\...\UsbDk_Package" -gg -sfrag -template fragment -out Files.wxs candle.exe Files.wxs Bundle.wxs light.exe -ext WixBalExtension Files.wixobj Bundle.wixobj -out Setup.exe ``` #### 5. **安装包验证** 安装后检查: 1. 程序文件目录:`C:\Program Files\UsbDk` 应包含: ``` UsbDkController.exe UsbDkHelper.dll include\*.h ``` 2. 系统驱动目录:`C:\Windows\System32\drivers` 应包含: ``` UsbDk.sys ``` 3. 使用`sigcheck`验证文件签名: ```cmd sigcheck -v C:\Windows\System32\drivers\UsbDk.sys ``` ### 关键注意事项 1. **驱动签名**: - 未签名的`.sys`文件会导致安装失败 - 需使用EV代码签名证书签名(参考[WHQL认证流程](https://docs.microsoft.com/zh-cn/windows-hardware/drivers)) 2. **版本兼容**: - 不同Windows版本需要不同的驱动签名 - 使用条件安装: ```xml <Condition Message="仅支持Windows 10+"> <![CDATA[VersionNT >= 1000]]> </Condition> ``` 3. **卸载处理**: 添加卸载脚本清理注册表项和配置文件: ```xml <CustomAction Id="CleanupReg" Script="vbscript" Execute="deferred"> <![CDATA[ Set fso = CreateObject("Scripting.FileSystemObject") fso.DeleteFile "C:\ProgramData\UsbDk\config.ini" ]]> </CustomAction> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值