zoukankan      html  css  js  c++  java
  • Codeforces #480 Tutorial

    Problem A,B,C:

    简单的模拟,注意A中p mod q时对q=0特殊处理(注意范围)


    Problem D:

    Brief Intro:

    给定长度为N的数组A,将A中所有连续子序列分成最少的组,使得每组任意一对数的积均为完全平方数

    求最终分成组数为K的子序列个数,K属于[1,N]

    Algorithm:

    能推出的性质:若P,Q两数积为完全平方数,则任意一个质因子的次幂的奇偶性必然相同

    那么想判断P,Q是否满足条件,只要保留每个质因子的次幂为0或1,再判断P,Q是否相同即可

    下面只要考虑如何O(N^2)地判断

    为了能O(1)判断新加入的数是否已经出现过,需要预处理出每一个数的上一个“自己”出现的位置

    由于数的范围过广,使用map记录一个数在检索到k时最后的位置

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int MAXN=5000+10;
    int n,dat[MAXN],res[MAXN],pre[MAXN];
    
    vector<int> prime;
    map<int,int> mp;
    
    bool isprime(int x)
    {
        int up_limit=sqrt(x);
        for(int i=2;i<=up_limit;i++)
            if(x%i==0) return false;
        return true;
    }
    
    void init()
    {
        int up_limit=sqrt(1e8+10);
        for(int i=2;i<=up_limit;i++)
            if(isprime(i)) prime.push_back(i);
    }
    
    int trans(int x)
    {
        int up_limit=sqrt(abs(x)),ret=x;
        for(int i=0;i<prime.size();i++)
        {
            if(prime[i]>up_limit || ret==1 || ret==-1) break;
            
            int t=ret,cnt=0;
            while(t%prime[i]==0) t/=prime[i],cnt++;
            if(cnt%2==1) ret=t*prime[i];
            else ret=t;
        }
        return ret;
    }
    
    int main()
    {
        cin >> n;
        for(int i=1;i<=n;i++)
            cin >> dat[i];
            
        init();
        
        for(int i=1;i<=n;i++) //质因数分解
            dat[i]=trans(dat[i]);
            
        for(int i=1;i<=n;i++)  //预处理pre
            if(mp.count(dat[i]))
            {
                pre[i]=mp[dat[i]];
                mp[dat[i]]=i;
            }
            else
            {
                pre[i]=-1;
                mp[dat[i]]=i;
            }
        
        for(int i=1;i<=n;i++)
        {
            bool f=true;int cnt=0;
            for(int j=i;j<=n;j++)
            {
                if(dat[j]) f=false;
                if(pre[j]<i && dat[j]) cnt++,mp[dat[j]]=true; //O(1)判断
                if(!f) res[cnt]++;
                else res[1]++; 
            }
        }
        
        for(int i=1;i<=n;i++) cout << res[i] << " ";
        return 0;
    }

    Review:

    1、特解:0

    在看到数据范围后,总要考虑特解。

    除非一段全部为0,否则忽略当前遇到的0

    2、积为完全平方数的性质:

    我当时只想到了传导性,反而忽略了每个质因子次幂奇偶性相同这一性质

    只考虑奇偶性   到    转化后判断相等的方法值得借鉴

    3、求解一串数中不同数的个数的预处理:

    求出每一个数前一次出现的位置     常用的预处理方式

    Problem E:

    一棵树中有N个点,每个点的权值为2^N

    要舍去K个点,使得这K个点的权值和最小,且剩下的点连通

    Algorithm:

    显而易见的贪心策略:

    反向求解,寻找n-k个要选的点

    由于第n个点的权值 > 1~n-1的权值和,所以从第n个点开始贪心选取即可

    为了将复杂度控制在 O(NlogN) ,使用树上倍增查找路径终点

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int MAXN=1e6+10;
    vector<int> G[MAXN],res;
    int n,k,f[MAXN][25],vis[MAXN],dep[MAXN];
    
    inline int read()
    {
        char ch;int f=0,num;
        while(!isdigit(ch=getchar())) f|=(ch=='-');
        num=ch-'0';
        while(isdigit(ch=getchar())) num=num*10+ch-'0';
        return f?-num:num;
    }
    
    void dfs(int cur,int anc) //初始化
    {
        dep[cur]=dep[anc]+1;f[cur][0]=anc;
        for(int i=1;i<=19;i++) f[cur][i]=f[f[cur][i-1]][i-1];
        
        for(int i=0;i<G[cur].size();i++)
        {
            int v=G[cur][i];
            if(v==anc) continue;
            dfs(v,cur);
        }
    }
    
    int main()
    {
        n=read();k=read();
        for(int i=1;i<n;i++)
        {
            int x=read(),y=read();
            G[x].push_back(y);G[y].push_back(x);
        }
        
        dfs(n,0);
        
        memset(vis,0,sizeof(vis));
        vis[n]=vis[0]=1;k=n-k-1;
        for(int i=n-1;i>=1;i--)
        {
            if(vis[i]) continue;
            
            int t=i;
            for(int j=19;j>=0;j--) //倍增找路径
                if(!vis[f[t][j]]) t=f[t][j];
            
            if(dep[i]-dep[t]+1<=k)
            {
                k-=(dep[i]-dep[t]+1);
                t=i;
                while(!vis[t]) vis[t]=1,t=f[t][0];
            }
            else res.push_back(i);
        }
        
        sort(res.begin(),res.end());
        for(int i=0;i<res.size();i++) cout << res[i] << " ";
        return 0;
    }

    Review:

    1、当正向贪心难以实现时,可以尝试反向贪心

    2、当要在树上O(logN)搜寻路径时,使用树上倍增法

  • 相关阅读:
    C#注释含义(XML注释)标签及其含义(二)
    Apache开启伪静态[转]
    C#注释含义(XML注释)标签及其含义(一)
    [转]终结PHP中文乱码的问题
    Symfony框架百科/项目实战/指南/教程
    第一次用Office2007写博客
    prototype.js是什么?
    优秀网站源码 集合
    Pager 精简的分页控件
    Validator.js 很好用的客户端表单验证
  • 原文地址:https://www.cnblogs.com/newera/p/9048713.html
Copyright © 2011-2022 走看看