铁一中模拟赛2018.11.1 T1

本文详细解析洛谷月赛P3794题目的解题思路与算法实现,通过分析数组中特定条件下的对数计数,利用ST表、二分查找和位运算解决复杂问题。

题目:

(Luogu月赛P3794)

给你一个长度为N的数组a,求有多少对i<=j满足 g c d ( a i , a i + 1 , a i + 2 , . . . , a j ) x o r ( a i o r a i + 1 o r a i + 2 o r . . . a j ) = k gcd(a_i,a_{i+1},a_{i+2},...,a_j)xor(a_i or a_{i+1} or a_{i+2} or ... a_j) =k gcd(ai,ai+1,ai+2,...,aj)xor(aiorai+1orai+2or...aj)=k

注: x o r , o r xor,or xor,or都指位运算

数据范围: n ≤ 500000 , 1 ≤ a [ i ] ≤ 500000 n\le 500000,1\le a[i]\le 500000 n500000,1a[i]500000

题解:

这个题思维难度其实不高主要是要:

  1. 有一个意识(也可以说是套路):求多个数的gcd时,添加一个数,gcd要么不变,要么降低,且最多降低 l o g n log n logn次就变成1

  2. 会二分求出像这样的单调函数的每个断层:

  3. 会灵活运用st表

显然说到这这题也就没什么可做的了,用st表维护区间Gcd和Or值。对于每个区间左端点,Gcd的值都为单调递减断层函数(且最多有log n个断层),Or的值都为单调递增断层函数。直接枚举区间左端点,二分求出Gcd的每个断层,因为 G c d x o r O r = k Gcd xor Or=k GcdxorOr=k,所以 O r = k G c d Or=k^Gcd Or=kGcd,就可以二分找到每个Gcd断层对应的Or断层,然后根据两个断层统计答案即可。

时间复杂度: O ( n l o g 2 n ) O(nlog^2 n) O(nlog2n)

代码:

#include <iostream> 
#include <stdio.h>
#include <algorithm>
using namespace std;
#define N 500005
int n,k;
int ai[N],Gcd[N][25],Or[N][25],Log[N];
long long ans;

inline int getGcd(int x,int y) //O(1)求[x,y]的Gcd
{
    int a=Log[y-x+1];
    return __gcd(Gcd[x][a],Gcd[y-(1<<a)+1][a]);
}

inline int getOr(int x,int y) //O(1)求[x,y]的Or 
{
    int a=Log[y-x+1];
    return (Or[x][a]|Or[y-(1<<a)+1][a]);
}

int main()
{
    scanf("%d%d",&n,&k);
    int np=0;
    for(int i=1;i<=n;i++){
        if(i==(1<<np))
            Log[i]=np++;
        else
            Log[i]=np-1;
    }
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        Or[i][0]=Gcd[i][0]=x;
    }
    for(int a=1;a<=20;a++)
        for(int i=1;i+(1<<a)-1<=n;i++)
        {
            Gcd[i][a]=__gcd(Gcd[i][a-1],Gcd[i+(1<<(a-1))][a-1]);
            Or[i][a]=Or[i][a-1]|Or[i+(1<<(a-1))][a-1];
        }
    for(int now=1;now<=n;now++){ //固定左指针为now
        int nowGcd=Gcd[now][0],rpGcd=now; //now为目前右指针范围到左指针的值,rp为目前右指针范围的左边界
        while(true)
        {
            int l=rpGcd,r=n;
            while(l<r) //二分Gcd右指针范围的右边界 (Gcd右指针递减)
            {
                int mid=((l+r)>>1);
                if(r==l+1) mid++;
                int tnGcd=getGcd(now,mid);
                if(tnGcd<nowGcd)
                    r=mid-1;
                else
                    l=mid;
            }
            int nowOr=(k^nowGcd),yoGcd=l,rpOr,yoOr;
/**/
            l=now,r=n;
            while(l<r) //二分Or右指针范围的左边界 (Or右指针递增)
            {
                int mid=((l+r)>>1);
                int tnOr=getOr(now,mid);
                if(tnOr>nowOr)
                    r=mid-1;
                else if(tnOr==nowOr)
                    r=mid;
                else
                    l=mid+1;
            }
            rpOr=l,r=n;
            while(l<r) //二分Or右指针范围的右边界 (Or右指针递增)
            {
                int mid=((l+r)>>1);
                if(r==l+1) mid++;
                int tnOr=getOr(now,mid);
                if(tnOr>nowOr)
                    r=mid-1;
                else
                    l=mid;
            }
            yoOr=l;
            int zuo=max(rpGcd,rpOr),you=min(yoGcd,yoOr);
            if(you>=zuo && getOr(now,rpOr)==nowOr) ans+=you-zuo+1;
            rpGcd=yoGcd+1;
            if(rpGcd>n) break;
            nowGcd=getGcd(now,rpGcd);
        }
    }
    cout<<ans<<endl;
        
    return 0;
}
<think> 我们之前提供的方案一中,使用`clip.name`应该直接得到的是动画片段的名称(如"Idle", "Run"等),而不是完整路径。如果用户遇到`clip.name`显示为路径名,那可能是因为在特殊情况下动画片段被命名为了路径(这种情况非常罕见)。通常,`AnimationClip`的`name`属性就是动画片段的名称。 但是,如果用户确实遇到了获取的是路径而不是文件名的情况,我们可以通过以下步骤进行修正: 1. 首先确认问题:打印出的`clip.name`是类似"Assets/Models/character.fbx|Run"这样的字符串,还是只是"Run"? 2. 如果打印的是包含路径的字符串,那么我们可以通过字符串处理提取出最后的动画名称。 在Unity中,当动画片段是从FBX中导入时,其名称通常是干净的(不包含路径)。但是,有时在旧版Unity或特殊情况下,可能会包含FBX文件名作为前缀(如"character@Run")。不过,我们之前的方法在正常情况下应该只返回动画名称。 如果用户确实得到的是完整路径,我们可以这样处理: 修改方案一代码,使用`System.IO.Path`来提取文件名: ```csharp using UnityEditor; using UnityEngine; using System.IO; // 添加此命名空间 [MenuItem("Tools/打印FBX动画名称")] public static void PrintFBXAnimationNames() { string fbxPath = AssetDatabase.GetAssetPath(Selection.activeObject); if (!fbxPath.EndsWith(".fbx")) { Debug.LogError("请选择FBX文件"); return; } Object[] assets = AssetDatabase.LoadAllAssetsAtPath(fbxPath); foreach (Object asset in assets) { if (asset is AnimationClip clip) { // 正常情况下,clip.name就是动画名 string animationName = clip.name; // 如果发现动画名包含路径(例如包含斜杠),则提取最后一部分 if (animationName.Contains("/")) { animationName = Path.GetFileName(animationName); } Debug.Log($"动画名称: {animationName}"); } } } ``` 但是,更常见的情况是,动画名称可能带有FBX文件名作为前缀,例如:FBX文件名是"character.fbx",动画名称为"character|Run"。这时,我们可以按如下方式分割: ```csharp // 如果动画名称包含管道符(|),则分割取后半部分 string[] parts = animationName.Split(&#39;|&#39;); if (parts.Length > 1) { animationName = parts[1]; } ``` 因此,我们可以将两种处理方式结合起来: ```csharp string animationName = clip.name; // 处理可能包含管道符的情况(旧版Unity或某些导出设置) if (animationName.Contains("|")) { // 分割并取最后一部分 animationName = animationName.Split(&#39;|&#39;)[1]; } // 处理包含路径的情况(虽然不太可能,但为了健壮性) else if (animationName.Contains("/")) { animationName = Path.GetFileName(animationName); } Debug.Log($"动画名称: {animationName}"); ``` 然而,在大多数情况下,我们并不需要这些处理。因为Unity导入FBX后,动画片段资源的名称就是动画片段的原始名称(不包含路径和FBX文件名)。所以,如果用户遇到的是路径,可能是其他原因造成的。 如果用户使用的是方案二(通过ModelImporter),则获取的`ModelImporterClipAnimation.name`就是动画片段的名称,不会包含路径。 因此,我建议用户先确认打印出的`clip.name`到底是什么格式。如果是包含路径,则按照上述方法处理;如果只是普通的动画名称,则不需要。 另外,注意:在Unity中,同一个FBX文件可能包含多个动画片段,每个动画片段都是作为子资源存在的,它们的名称应该就是动画片段名称。 如果问题仍然存在,请提供打印出的具体字符串,以便进一步分析。 现在,根据用户的问题,我们假设确实遇到了路径,下面给出完整的修改后的方案一代码: ```csharp using UnityEditor; using UnityEngine; using System.IO; [MenuItem("Tools/打印FBX动画名称")] public static void PrintFBXAnimationNames() { // 确保选中了对象 if (Selection.activeObject == null) { Debug.LogError("请选择一个FBX文件"); return; } string fbxPath = AssetDatabase.GetAssetPath(Selection.activeObject); if (string.IsNullOrEmpty(fbxPath) || !fbxPath.EndsWith(".fbx")) { Debug.LogError("请选择FBX文件"); return; } Object[] assets = AssetDatabase.LoadAllAssetsAtPath(fbxPath); foreach (Object asset in assets) { if (asset is AnimationClip clip) { string animationName = clip.name; // 处理管道符格式(例如:character|Run) if (animationName.Contains("|")) { // 取管道符后面的部分 animationName = animationName.Substring(animationName.LastIndexOf(&#39;|&#39;) + 1); } // 处理路径格式(例如:Assets/.../Run) else if (animationName.Contains("/")) { // 取最后一个斜杠后面的部分 animationName = animationName.Substring(animationName.LastIndexOf(&#39;/&#39;) + 1); } Debug.Log($"动画名称: {animationName}"); } } } ``` 这样,无论动画名称中是否包含管道符或路径分隔符,我们都能提取出纯粹的动画名称。 注意:如果动画名称中同时包含管道符和路径分隔符,上述代码会先处理管道符,所以不会进入路径处理分支。因为我们先判断了管道符的存在。如果用户遇到更复杂的情况,可能需要调整。 最后,建议用户优先使用方案二(ModelImporter)来获取动画片段,因为这样可以直接获取到导入设置中的动画片段名称,并且不会出现路径问题。 §§相关问题§§ 1. 如何批量修改FBX中所有动画片段的名称? 2. 如何为FBX中的动画片段添加前缀? 3. 如何将动画片段从FBX文件中提取为独立的AnimationClip文件? </think> # 解决动画打印中 `clip.name` 显示为路径名的问题 在 Unity 中,当使用 `AssetDatabase.LoadAllAssetsAtPath()` 方法时,有时会遇到 `clip.name` 显示为完整路径而非动画文件名的问题。以下是三种完整的解决方案: ## 解决方案一:使用 Path.GetFileNameWithoutExtension 方法(推荐) ```csharp using UnityEditor; using UnityEngine; using System.IO; // 添加此命名空间 using System.Collections.Generic; [MenuItem("Tools/打印FBX动画名称")] public static void PrintFBXAnimationNames() { string fbxPath = AssetDatabase.GetAssetPath(Selection.activeObject); if (!fbxPath.EndsWith(".fbx")) { Debug.LogError("请选择FBX文件"); return; } // 关键修改:提取文件名(不含扩展名) string fileName = Path.GetFileNameWithoutExtension(fbxPath); Object[] assets = AssetDatabase.LoadAllAssetsAtPath(fbxPath); foreach (Object asset in assets) { if (asset is AnimationClip clip) { // 关键修改:拼接文件名和动画片段名 string animationName = $"{fileName}_{clip.name}"; Debug.Log($"动画名称: {animationName}"); } } } ``` **核心修改说明**: 1. 添加 `System.IO` 命名空间以使用 `Path.GetFileNameWithoutExtension()` 2. 使用 `Path.GetFileNameWithoutExtension(fbxPath)` 提取 FBX 文件名 3. 将文件名与动画片段名组合:`$"{fileName}_{clip.name}"` 4. 这样输出的格式为:`动画名称: Character_Run`(假设 FBX 文件名为 Character.fbx) ## 解决方案二:使用 ModelImporter 获取干净名称 ```csharp using UnityEditor; using UnityEngine; [MenuItem("Tools/打印FBX动画详情")] public static void PrintFBXAnimationDetails() { string fbxPath = AssetDatabase.GetAssetPath(Selection.activeObject); if (!fbxPath.EndsWith(".fbx")) { Debug.LogError("请选择FBX文件"); return; } ModelImporter importer = AssetImporter.GetAtPath(fbxPath) as ModelImporter; if (importer != null) { // 关键修改:ModelImporterClipAnimation.name 直接提供干净名称 foreach (ModelImporterClipAnimation clip in importer.clipAnimations) { Debug.Log($"动画名称: {clip.name}"); } } } ``` **核心修改说明**: 1. 使用 `ModelImporterClipAnimation` 的 `name` 属性直接获取动画文件名 2. 这种方法不包含路径信息,直接输出干净的名称如 `Run`、`Attack` 3. 这是 Unity 推荐的获取动画片段名称的方式[^1] ## 解决方案三:运行时获取文件名(不含路径) ```csharp using UnityEngine; public class RuntimeAnimationPrinter : MonoBehaviour { void Start() { Animator animator = GetComponent<Animator>(); if (animator != null && animator.runtimeAnimatorController != null) { AnimationClip[] clips = animator.runtimeAnimatorController.animationClips; foreach (AnimationClip clip in clips) { // 关键修改:直接输出动画文件名 Debug.Log($"动画文件: {clip.name}"); } } } } ``` **核心修改说明**: 1. 在运行时直接访问 `AnimationClip` 的 `name` 属性 2. 运行时动画资源的 `name` 属性不包含路径信息 3. 输出格式为 `动画文件: Run` ## 使用说明 1. **方案选择指南**: - 在编辑器中使用:选择方案一或方案二 - 在游戏运行时使用:选择方案三 - 需要额外元数据:选择方案二 2. **常见问题处理**: - 如果动画片段名称为空,检查FBX导入设置中的"动画"选项卡 - 如果遇到`MissingComponentException`,确保游戏对象上有Animator组件 - 对于"Take 001"等默认名称,可在FBX导入设置中重命名动画片段[^3] 3. **性能优化**: ```csharp // 批量处理多个FBX文件 string[] allFbxPaths = Directory.GetFiles("Assets/Models", "*.fbx"); foreach (string path in allFbxPaths) { // 处理每个FBX文件... } ``` > 提示:使用 `ModelImporterClipAnimation` 是最可靠的方式,因为它直接提供导入设置中定义的动画名称,不受资源路径影响[^1][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值