Codeforces802 I. Fake News (hard) (后缀数组height[]+单调栈)

题意:

给定字符串s,
对于s中每一种本质不同的串,设出现次数为cnt,那么对答案的贡献为cnt2
要求计算所有本质不同的串的贡献和

数据范围:|s|<=1e5

解法:
看了别人的题解,是将贡献变为:
每个子串和之前出现的相同子串匹配(不包括自己),
设匹配次数为cnt,则贡献为2*cnt+1

为什么可以这样呢?
1.先验证一下是否正确:
假设串为aaa,现在要计算子串a的贡献
第一个a的贡献为2*0+1=1,
第二个a的贡献为2*1+1=3,
第三个a的贡献为2*2+1=5,
总贡献为1+3+5=9,而按平方算贡献的话,贡献为3^2=9,是一样的.
2.原理:
将n^2拆分成了1+3+5+7+9...这个等差数列,
这个等差数列的前n项和恰好是n^2
而第i项的通项公式为(i-1)*2+1,(也可以是2*i-1,做法应该差不多)
(学到了新操作)

把贡献中的+1提出来,每一步的贡献2*cnt+1就变为2*cnt,
设总出现次数为tot,那么提出来的+1部分的总贡献为tot

所有本质不同子串的tot部分总和为n*(n+1)/2,可以直接算
(因为出现tot次,贡献为tot,即每个子串的贡献为1,子串总数就是贡献数)

2*cnt部分的总贡献,用单调递增栈计算就行了,
做法是后缀数组+单调栈的套路了(height[]+单调栈).
这里就不细说了.
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxm=1e5+5;
struct SA{
    static const int N=2e6+5;
    char s[N];
    int sa[N],rk[N],oldrk[N<<1],id[N],px[N],cnt[N];
    int ht[N];
    int n;//字符串长度
    int m=300;//初始字符集大小
    bool cmp(int x,int y,int w){
        return oldrk[x]==oldrk[y]&&oldrk[x+w]==oldrk[y+w];
    }
    void init(){//init
        memset(cnt,0,sizeof cnt);
        m=300;
    }
    void getSA(){
        int i,p,w;
        for(i=1;i<=n;i++)cnt[rk[i]=s[i]]++;
        for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
        for(i=n;i>=1;i--)sa[cnt[rk[i]]--]=i;
        //
        for(w=1;w<n;w<<=1,m=p){
            for(p=0,i=n;i>n-w;i--)id[++p]=i;
            for(i=1;i<=n;i++)if(sa[i]>w)id[++p]=sa[i]-w;
            memset(cnt,0,sizeof cnt);
            for(i=1;i<=n;i++)cnt[px[i]=rk[id[i]]]++;
            for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
            for(i=n;i>=1;i--)sa[cnt[px[i]]--]=id[i];
            memcpy(oldrk,rk,sizeof rk);
            for(p=0,i=1;i<=n;i++){
                rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
            }
        }
    }
    void getHT(){
        for(int i=1,k=0;i<=n;i++){
            if(k)k--;
            while(s[i+k]==s[sa[rk[i]-1]+k])k++;
            ht[rk[i]]=k;
        }
    }
}sa;
struct Node{
    int h,len;
}stk[maxm];
signed main(){
    //input
    int T;scanf("%d",&T);
    while(T--){
        sa.init();
        //
        char *s=sa.s;
        int &n=sa.n;
        scanf("%s",s+1);
        n=strlen(s+1);
        //
        sa.getSA();
        sa.getHT();
        //
        int *ht=sa.ht;
        ll ans=0;
        ans+=1ll*n*(n+1)/2;
        ll cnt=0;
        int head=0;
        for(int i=2;i<=n;i++){
            Node temp={ht[i],1};
            while(head&&stk[head].h>temp.h){
                temp.len+=stk[head].len;
                cnt-=1ll*stk[head].h*stk[head].len;
                head--;
            }
            stk[++head]=temp;
            cnt+=1ll*temp.h*temp.len;
            ans+=cnt*2;
        }
        cout<<ans<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图像识别技术在病虫害检测中的应用是一个快速发展的领域,它结合了计算机视觉和机器学习算法来自动识别和分类植物上的病虫害。以下是这一技术的一些关键步骤和组成部分: 1. **数据收集**:首先需要收集大量的植物图像数据,这些数据包括健康植物的图像以及受不同病虫害影响的植物图像。 2. **图像预处理**:对收集到的图像进行处理,以提高后续分析的准确性。这可能包括调整亮度、对比度、去噪、裁剪、缩放等。 3. **特征提取**:从图像中提取有助于识别病虫害的特征。这些特征可能包括颜色、纹理、形状、边缘等。 4. **模型训练**:使用机器学习算法(如支持向量机、随机森林、卷积神经网络等)来训练模型。训练过程中,算法会学习如何根据提取的特征来识别不同的病虫害。 5. **模型验证和测试**:在独立的测试集上验证模型的性能,以确保其准确性和泛化能力。 6. **部署和应用**:将训练好的模型部署到实际的病虫害检测系统中,可以是移动应用、网页服务或集成到智能农业设备中。 7. **实时监测**:在实际应用中,系统可以实时接收植物图像,并快速给出病虫害的检测结果。 8. **持续学习**:随着时间的推移,系统可以不断学习新的病虫害样本,以提高其识别能力。 9. **用户界面**:为了方便用户使用,通常会有一个用户友好的界面,显示检测结果,并提供进一步的指导或建议。 这项技术的优势在于它可以快速、准确地识别出病虫害,甚至在早期阶段就能发现问题,从而及时采取措施。此外,它还可以减少对化学农药的依赖,支持可持续农业发展。随着技术的不断进步,图像识别在病虫害检测中的应用将越来越广泛。
单调栈是一种常用的数据结构,用于解决一类特定的问题,其中最常见的问题是找到数组中每个元素的下一个更大或更小的元素。在Codeforces编程竞赛中,单调栈经常被用于解决一些与数组相关的问题。 下面是单调栈的一般思路: 1. 创建一个空栈。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将当前元素入栈。 4. 重复步骤3,直到遍历完所有元素。 这样,最后剩下的栈中元素就是没有下一个更大或更小元素的元素。在使用单调栈求解具体问题时,我们可以根据需要进行一些特定的操作。 例如,如果要找到一个数组中每个元素的下一个更大的元素,可以使用单调递减栈。具体操作如下: 1. 创建一个空栈和一个空结果数组。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将其在结果数组中的位置记录为当前元素的下一个更大元素的索引。 4. 将当前元素入栈。 5. 重复步骤3和4,直到遍历完所有元素。 6. 结果数组中没有下一个更大元素的位置,可以设置为-1。 以下是一个使用单调递减栈求解下一个更大元素问题的示例代码: ```cpp #include <iostream> #include <stack> #include <vector> std::vector<int> nextGreaterElement(std::vector<int>& nums) { int n = nums.size(); std::vector<int> result(n, -1); std::stack<int> stack; for (int i = 0; i < n; i++) { while (!stack.empty() && nums[i] > nums[stack.top()]) { result[stack.top()] = i; stack.pop(); } stack.push(i); } return result; } int main() { std::vector<int> nums = {1,3, 2, 4, 5, 1}; std::vector<int> result = nextGreaterElement(nums); for (int i = 0; i < result.size(); i++) { std::cout << "Next greater element for " << nums[i] << ": "; if (result[i] != -1) { std::cout << nums[result[i]]; } else { std::cout << "None"; } std::cout << std::endl; } return 0; } ``` 以上代码将输出: ``` Next greater element for 1: 3 Next greater element for 3: 4 Next greater element for 2: 4 Next greater element for 4: 5 Next greater element for 5: None Next greater element for 1: None ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值