zoukankan      html  css  js  c++  java
  • NOIP2018提高组省一冲奖班模测训练(五)

    NOIP2018提高组省一冲奖班模测训练(五)

    http://www.51nod.com/Contest/ContestDescription.html#!#contestId=79

    今天有点浪……

    第一题想了一个多小时想到了正解,然后敲到一半就去看lol总决赛了(恭喜IG!!!!!!)

    然后就没有然后了……

    A 循环

     小D站在一个长度为n的环,环上的位置从0到n-1编号。位置0与位置n-1相邻。对于一个位置i,

    小D只能跳到距离位置i不超过R[i]的位置上。

    可以顺时针也可以逆时针跳。比如当n=5,R[1]=2时,小D可以跳到的位置集合为{4,0,1,2,3}。 一开始小D站在位置s,

    他的终点是位置t。假设每跳一次需要单位时间1。你能求出小D跳到终点的最少需要时间吗?假如无解则输出-1。

     注意因为输入规模比较大,R由以下方式生成。 

    R[i]=(R[i-1]×g+seed)mod~p 

    其中R[0],g,seed,p是给定的。 

     

    输入

    第一行给定三个整数,n,s,t。
    第二行给定四个整数R[0],g,seed,p。
    
    
    【数据规模与约定】
    对于20%的数据,n≤100
    对于40%的数据,n≤1000
    对于60%的数据,1≤n≤10^5
    对于100%的数据,1≤n≤10^7,0≤s,t<n,1≤p≤n,0≤R[0],g,seed<p

    输出

    输出一行一个整数,表示最短时间。

    输入样例

    9 0 2 
    1 3 4 7

    输出样例

    2


    
    

     刚看到这道题,我发现我好像很久很久很久以前做过???

    不过肯定当时是抄题解的,印象不深,没怎么理解。

    或者其实我没有做过,我自己yy罢了

     

    首先我一开始去观察那个公式的性质

    然后发现没什么规律

    貌似就是为了输入方便……

     

    然后我就考虑一个点所能达到的区间

    画个图出来发现貌似是用贪心解决区间问题???

    然后仔细想想好像不是

    一个很显然的思路就是每次要跳到最远

    然后我就画了几下,发现貌似之前搜过的点就不用再搜了

    所以每一个点最多会被检查过一次,复杂度是O(n)的,可以过1e7的数据

     

    但是我发现这涉及到跨越区间的问题(如从n-1跳到0),不好搞

    而且s和t的大小关系也不确定

    感觉不好实现,感觉会比较复杂

    然后就硬着头皮去写,写了没多久,一看时间哇靠比赛要开始了,然后……

     

    后来讲解我看到了标程的写法,在一些处理上非常有技巧性

    我需要学习学习。代码有注释

    #include<bits/stdc++.h>
    #define REP(i, a, b) for(register int i = (a); i < (b); i++) 
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++) 
    using namespace std;
    
    const int MAXN = 1e7 + 10;
    int R[MAXN], a[MAXN], n, s, t;
    int g, seed, p, ans;
    bool val[MAXN];
    
    int main()
    {
        scanf("%d%d%d%d%d%d%d", &n, &s, &t, &R[0], &g, &seed, &p);
        REP(i, 1, n) R[i] = (1ll * R[i-1] * g + seed) % p; 
        REP(i, s, n) a[i - s] = R[i]; //a数组的作用就是把整个数组等价转化成以0为起点,方便后面处理 
        REP(i, 0, s) a[i - s + n] = R[i];
        t = (t - s + n) % n; //注意这里s和t大小不确定 
        
        int l = 0, r = 0;  
        _for(ans, 0, n)
        {
            if(l + n <= t || t <= r) { printf("%d
    ", ans); return 0; } //注意这里是或,可以画图理解 
            int pl = l, pr = r; //注意l是负的,但是一旦作为数组的下标就要加上n 
            _for(i, pl, 0)  
            {
                if(val[i + n]) break; //val[i]表示i这个点有没有遍历过 
                l = min(l, i - a[i + n]), r = max(r, i + a[i + n]), val[i + n] = 1;
            }
            for(int i = pr; i >= 0; i--)
            {
                if(val[i]) break;
                l = min(l, i - a[i]), r = max(r, i + a[i]), val[i] = 1;
            }
        }
        puts("-1");
    
        return 0;
    }

     小D的老师给了小D一大堆的字符串。具体来说,小D现在得到了N个字符串, 第i个字符串为S_i。对于小D来说,

    他最喜欢的字符串就是回文串了。 一个串是回文串,当且仅当它倒过来和本身相同。比如abaaba倒过来还是abaaba,

    而xyz倒过来是zyx,所以abaaba是回文串而xyz不是。 小D是个有创造力的孩子。他决定尝试把这些串两两拼接。

    假如一次拼接后得到的串是回文的,他会非常开心。现在他想问你他最多能开心多少次?

    形式化来说,小D想知道,有多少对二元组(i,j),满足i≠j,S_i+S_j是回文串。其中+表示字符串的前后拼接。

    比如aa+ab=aaab。注意假如(i,j)和(j,i)都是满足条件的,那么答案算两次。 

    输入

    第一行包含一个整数n。
    接下来n行,每行给定一个字符串S_i。
    
    【数据规模与约定】
    对于20%的数据,字符串总长不超过2000。
    对于40%的数据,n≤1000
    另外有20%的数据,所有字符串长度均相同。
    对于100%的数据,1≤n≤10^5,字符串总长度不超过10^5。字符串只包含小写字母。

    输出

    输出一个整数,表示答案。

    输入样例

    8
    abaccabaab
    accaba
    abacc
    ab
    ba
    aba
    ccaba
    abaaba

    输出样例

    14

    这道题好艰辛……
    写了N久

    看到题目,字符总长已经提示会用到trie了
    而且1e5要不是nlogn,要不是n
    而有关字符串里面复杂度是O(n)的很容易想到trie
    提示已经很明显了

    正解是trie+哈希,非常的巧妙
    我们考虑两个字符串
    ab
    ccba
    这两个拼起来是abccba是一个回文串
    那么回文串去掉两端也是回文串
    所以我们把ccba拆成cc ba
    可以用trie判断ab和ba,用哈希判断cc是否是回文串(正串与反串的哈希值相等即为回文串)
    这是大致思路

    那么我们可以预处理每个串的每个前缀是不是回文串,用哈希,复杂度为字符总数
    然后每次把当前串倒着去trie上遍历,如果遍历一个位置,之前是回文串,那么更新答案
    然后再加入当前串

    如果我们按照下标的顺序去这样处理的话
    可以发现会漏掉一些情况
    如果这样得出的字符满足
    i < j ,i + j是回文串
    显然还有三种情况
    i < j ,j + i是回文串
    i > j ,i + j是回文串
    i > j ,j + i是回文串
    对于i > j可以改变枚举的顺序
    对于j+i,可以把串取反再去做(这个时候如果两个串长度相同会算两次,要特判)
    因为如果j + i是回文串
    那么i取反的ii,j取反的jj
    ii + jj也是回文串
    很神奇,其实可以证明
    比如ab + ccba
    取反交换相加即
    abcc + ba
    也是回文串
    #include<bits/stdc++.h>
    #define REP(i, a, b) for(register int i = (a); i < (b); i++)
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++)
    using namespace std;
    
    typedef unsigned long long ull;
    const int MAXN = 1e5 + 10;
    const int base = 131;
    
    int trie[MAXN][26], tot;
    int flag[MAXN], n;
    string s[MAXN];
    ull p[MAXN], hash[MAXN][2], cnt[MAXN], ans; 
    
    void init()
    {
        tot = 1;
        memset(cnt, 0, sizeof(cnt));
        memset(trie, 0, sizeof(trie));
    }
    
    void Insert(string s)
    {
        int p = 1, len = s.length();
        REP(i, 0, len)
        {
            int ch = s[i] - 'a';
            if(!trie[p][ch]) trie[p][ch] = ++tot;
            p = trie[p][ch];
        }
        cnt[p]++;
    }
    
    void Search(string s, int op)
    {
        int p = 1, len = s.length();
        for(int i = len - 1; i >= 0; i--)
        {
            int ch = s[i] - 'a';
            if(!trie[p][ch]) return;
            p = trie[p][ch];
            if((op || i) && (!i || flag[i - 1])) ans += cnt[p];
        }
    }
    
    inline ull val(int l, int r, int k)
    {
        return hash[r][k] - (l - 1 < 0 ? 0 : hash[l-1][k]) * p[r - l + 1];
    }
    
    void solve(int op)
    {
        init();
        _for(k, 1, n)
        {
            string str = s[k];
            int len = str.length();
            hash[0][0] = str[0] - 'a';
            hash[0][1] = str[len-1] - 'a';
            
            REP(i, 1, len)
            {
                hash[i][0] = hash[i-1][0] * base + str[i] - 'a';
                hash[i][1] = hash[i-1][1] * base + str[len-i-1] - 'a';
            }
            REP(i, 0, len) flag[i] = (val(0, i, 0) == val(len - i - 1, len - 1, 1) ? 1 : 0);
            
            Search(str, op);
            Insert(str);
        }
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin >> n; 
        _for(i, 1, n) cin >> s[i];
        p[0] = 1; _for(i, 1, MAXN) p[i] = p[i-1] * base;
        
        solve(1); 
        reverse(s + 1, s + n + 1); solve(1); 
        _for(i, 1, n) reverse(s[i].begin(), s[i].end()); solve(0);
        reverse(s + 1, s + n + 1); solve(0);
        
        printf("%llu
    ", ans);
        return 0;
    }
    第三题不会,不想改了……

    
    


  • 相关阅读:
    O(n^2)的排序方法
    99乘法表
    excel 转 csv
    批量关闭 excel
    tomcat 加入服务
    文件打包 zip
    字符串转换
    List数组种删除数据
    mybatis 批量上传
    sql server 查询表字段及类型
  • 原文地址:https://www.cnblogs.com/sugewud/p/9902468.html
Copyright © 2011-2022 走看看