• 沈阳集训day3


    问题 A: 老光棍

    题目描述

    L君和G君在玩一个游戏。G君写下一个字符串A,L君将其复制一遍连接到A串后面得到字符串B,G君又在B的任意位置(包括首尾)插入一个字符得到字符串C。现在你得到了C,请你找出A。

    输入

    第一行一个整数T,表示有T组数据
    每组数据第一行一个整数 n,表示字符串 C 的长度。
    每组数据第二行 n 个大写字母,表示字符串 C。

    输出

    若不存在可能的字符串 A,输出一行“NOT POSSIBLE” ;若存在多个不同的字符串 A 满足条件,输出一行“NOT UNIQUE” ;否则输出一行为满足条件的字符串 A。

    样例输入

    1
    5
    ZGZGZ

    样例输出

    NOT UNIQUE

    提示

    对于50%的数据,满足2 ≤ n ≤ 2000。

    对于100%的数据,满足T ≤ 10,2 ≤ n ≤ 1000001。

    部分数据随机。
    题解:O(n) 枚举长度, O(1)hash查询是否满足, 如果前半部分可以去掉一个, 后半部分也可以去掉一个, 那么还要比较两个模板是否一样, 那么要去掉,判断细节比较多
    #include <iostream>
    #include<cstdio>
      
    using namespace std;
    const int bas = 2333, maxn = 1000005;
    int n, rightpos,has[maxn], base[maxn];
    char s[maxn];
    void init(){
        base[0] = 1;
        for(int i = 1; i <= n; i++)
            has[i] = has[i-1] * bas + s[i-1], base[i] = base[i-1] * bas;
    }
    bool check(int pos){
        int st1, ed1, a, b , c, d;
        if(pos == 1){
            a = has[n/2+1]-has[1]*base[n/2], c = has[n] - has[n/2+1]*base[n/2];
            b = d = 0;
        }
        else if(pos == n){
            a = has[n/2], c = has[n-1] - has[n/2] * base[n/2];
            b = d = 0;
        }
         else if(pos == n/2 +1){
            a = has[n/2], c = has[n] - has[n/2+1]*base[n-n/2-1];
            b = d = 0;
        }
        else if(pos <= n/2){
            st1 = n/2 + 1;
            ed1 = n/2 + pos,
            a = has[pos-1], b = has[n/2+1] - has[pos]*base[n/2-pos+1];
            c = has[ed1] - has[st1]*base[ed1-st1], d = has[n] - has[ed1]*base[n-ed1];
        }
        else if(pos > n/2){
            st1 = pos-n/2-1;
            a = has[st1], b = has[n/2] - has[st1]*base[n/2-st1];
            c = has[pos-1] - has[n/2]*base[pos-1-n/2], d = has[n]-has[pos]*base[n-pos];
        }
         
        if((a==c) && (b==d)){
            rightpos = pos;return 1;
        }
        return 0;
      
      
    }
    int main()
    {
        int T;
        scanf("%d", &T);
        while(T--){
            int cnt = 0;
            scanf("%d", &n);
            scanf("%s", s);
            init();
            int fg1 = 0, fg2 = 0;
            if(n%2 == 0){
                puts("NOT POSSIBLE");
                continue;
            }
            for(int i = 1; i <= n/2; i++)
                if(check(i)){
                    fg1 = 1;break;
                }
            for(int i = n/2+2; i <= n; i++)
                if(check(i)){
                    fg2 = 1; break;
                }
             
            if(fg1 && fg2)
                if(check(n/2+1)) cnt = 1;
                    else cnt = 2;
            else if(fg1 || fg2) cnt = 1; 
             
            if(check(n/2+1))cnt = 1;
            if(cnt == 1){
                if(rightpos <= n/2){
                     for(int i = 1; i <= n/2+1; i++)
                         if(i!=rightpos)printf("%c",s[i-1]);
                }      
               else {
                   for(int i = n/2+1; i <= n; i++)
                      if(i!=rightpos)printf("%c", s[i-1]);    
                }
                    printf("
    ");   
            }
            else if(cnt > 1)puts("NOT UNIQUE");
            else puts("NOT POSSIBLE");
        }
    }
    View Code

    问题 B: 饼干

    题目描述

    佩琪和贝茜是好朋友。
    佩琪最喜欢吃饼干了!但是现在他不准备自己吃,她决定和贝茜分享自己的饼干。佩琪有四种口味的饼干,分别是巧克力味、香草味、红豆味和椰子味。可惜的是,佩琪已经把椰子味的饼干全吃光了,所以现在只剩下前三种了233。。。
    巧克力味饼干能带来A的美味度,香草味饼干能带来B的美味度,而红豆味饼干可以带来A+B的美味度。
    佩琪会拿出不多于N块饼干作为送给贝茜的礼物。
    为了显得真诚,她想让她拿出的饼干的美味度之和恰好为K。
    为了显得体面,她决定用一个精美的盒子来装这些饼干。盒子内部有N个位置,每个位置都可以放一块饼干,或者不放。
    现在佩琪想知道有多少种方案可以既显得真诚又显得体面。两种方案不同,当且仅当盒子中某个位置放置的饼干味道不同,或者一个放了饼干而另一个没有。
    佩琪自己当然不会做啦,所以她决定请你来帮她解决这个问题。为了防止答案过大,你只需要告诉她答案对998244353取模的结果就行了。

    输入

    输入只有一行,包含四个整数,分别为N、A、B、K。

    输出

    输出一行一个整数,即答案对998244353取模的结果。

    样例输入

    4 1 2 5

    样例输出

    40

    提示

    对于前30%的数据,1≤N≤10,0≤K≤500

    对于前50%的数据,1≤N≤1000,0≤K≤3000;

    对于前80%的数据,1≤N≤1e5,0≤K≤2e10;

    对于100%的数据,1≤N≤1e7,0≤K≤1e15,0≤A,B ≤1e5。

    题解:我们可以求出n个盘子里放m个苹果的方案, 美味度可以枚举a的数量,b的数量也确定了, 那么a+b怎么办呢?

    我们发现没有限制的放a, 没有限制的放b, 有些就会又放a又放b, 相当于a+b, 是不是很妙? 所以方案数 = C(a) * C(b) 。

    还要, 这又是一道细节多的要死的题;

    #include<iostream>
    #include<cstdio>
    #define p 998244353
    #define ll long long
    using namespace std;
    const int maxn = 1e7 + 5;
    ll fac[maxn];
    ll a, b, k, n;
    ll ans;
    ll mi(long long a,long long b)
    {
        long long anss=1;
        for(;b;b>>=1,a=a*a%p)
           if(b&1) anss=anss*a%p;
        return anss;
    }
    ll exgcd(ll a, ll b, ll &x, ll &y){
        if(b == 0){
            x=1;y=0;return a;
        }
        ll x0, y0;
        ll d = exgcd(b, a%b, x0, y0);
        x = y0;
        y = x0 - (a/b)*y0;
        return d;
    }
    ll in(ll a){
        ll x, y;
        exgcd(a, p, x, y);
        return (x%p + p) %p;
    }
    ll C(ll pp, ll q){
        return fac[n] * in(fac[pp]) % p * in(fac[n-pp])  % p * fac[n] %p * in(fac[q]) %p *in(fac[n-q]) % p;
    }
    int main ( ) {
        scanf ( "%lld%lld%lld%lld", &n, &a, &b, &k );
        fac[0] = 1;
        for(ll i = 1; i <= n; i++)
            fac[i] = fac[i-1]*i % p;
        if(a==0&&b!=0) swap(a,b);
        if(!a && !b&&k!=0) ;
        else if(!a&&!b&&!k)
          ans=mi(4,n);
        else if(!b){
            int fg = 0;
            if(k % a) fg = 1;
            int t = k/a;
            if(fg == 0)
                ans = fac[n] * in(fac[t]) % p * in(fac[n-t]) % p*mi(2,n)%p;
                     
        }
        else {
            for(ll i = 0; i <= n; i++)
                if(i * a > k)break;
                else if( (k - a*i) % b == 0){
                    ll t = (k - a*i) / b;
                    if(t <= n)ans = (ans + C(i , t)) % p;
                    //printf("%lld  ", ans);
                }   
        }
        printf ("%lld", ans);
        return 0;
    }
    View Code

    问题 C: 空间宝石

    题目描述

    zP1nG很清楚自己打不过灭霸,所以只能在自己出的题里欺负他。
    咳咳。
    这一次他得到了空间宝石Tesseract。
    世界之树连接着九界,此时灭霸和zP1nG都在九界的某个地方。而九界是相互无法到达的。zP1nG为了追杀灭霸,决定使用空间宝石的力量到达灭霸身边。因为zP1nG不擅长使用空间宝石,无法直接开一条从自己的位置到灭霸的位置的传送门,所以他只能无意识地使用空间宝石的力量。zP1nG想知道,在自己胡乱地使用空间宝石后,通过传送门到达灭霸的位置最少需要多长时间。
    具体地,九界的编号为0~8,共有n道传送门,第i道传送门为优先级为pi,由ui到vi,需要花费wi个时间的单向门。传送的规则为:zP1nG按顺序穿过的传送门的优先级必须是单调不降的。例如,zP1nG穿过了三道传送门到达灭霸的位置,这三道传送门的优先级分别为1→2→2即为一种合法的方式,而优先级分别为1→2→1是不合法的。
    zP1nG会使用q次宝石的力量来尝试进行传送:其中第i次尝试会打开数道传送门,这些传送门的优先级会形成si个区间。例如,在某次尝试中zP1nG打开了三个区间[1,2],[4,7],[9,10],那么优先级在这三个区间内的传送门将全部被打开并允许zP1nG穿过。你需要告诉zP1nG在此情况下从自己的位置zi到达灭霸所在处ti所需的最短时间。尝试结束后所有传送门会关闭

    输入

    第1行包含两个正整数n和S,S的含义见数据范围与约定所述。
      第2至n+1行每行包含4个正整数pi,ui,vi,wi。
      第n+2行包含一个正整数q。
      第n+3至第n+q+2行每行若干个正整数,其中前三个数为zi,ti,si,之后为2*si个正整数,表示每个区间的左右端点。
      各变量具体含义见题目描述所述。

    输出

    对于zP1nG进行的每次尝试,输出一行一个数表示从zP1nG的位置到灭霸的位置所需的最短时间,如果zP1nG无法到达灭霸的位置则输出-1。

    样例输入

    6 2
    1 2 4 1
    2 1 3 3
    3 1 2 2
    4 3 4 5
    5 2 4 3
    6 1 4 2
    4
    1 4 1 1 3
    1 4 1 1 4
    1 4 2 5 5 2 3
    1 4 1 1 6

    样例输出

     -1
     8
     5
     2

    提示

     

     
    题解:这是一个神奇的解法,我们发现矩阵乘法是
    a[i][j] = sum( b[i][k] + c[k][j] ); 如果把他改成a[i][j] = min( b[i][k] + c[k][j] ), 是不是就成Floyd了;
    表示从i 到 j的最短路;那么优先级怎么办?——线段树;
    每个优先级开一棵线段树,只能从优先级低的地方更新过来,所以询问的时候排一道序,然后用线段树合并,因为矩阵a[i][j]表示的是从i到j,所以必须要按优先级更新(思考为什么)
    #include <bits/stdc++.h>
     
    using namespace std;
    #define maxn 10005
    #define inf 1000000008
    const int lim = 2005;
    struct Q{
        int l, r;
    }q[maxn];
    /*struct Edge{
        int p, u, v, w;
    }e[maxn];
    bool cmp2(Edge a, Edge b){
        return a.p < b.p;
    }*/
    bool cmp(const Q &a,const Q &b){
        return a.l < b.l;
    }
     
    int tot;
    struct  Maxtri{
        int dis[10][10];
     
        void init(){
            for(int i = 0; i <= 8; i++)
                for(int j = 0; j <= 8; j++)
                    dis[i][j] = (i == j) ? 0 : inf;
        }
     
    }G[lim+1];
    Maxtri merge(Maxtri a, Maxtri b){
        Maxtri c;
        c.init();
        for(int k = 0; k < 9; k++)
            for(int i = 0; i < 9; i++)
                for(int j = 0; j < 9; j++)
                    c.dis[i][j] = min(c.dis[i][j], a.dis[i][k]+b.dis[k][j]);
        return c;
     
    };
     
     
    struct Node {
        Node * ls, * rs;
        Maxtri now;
    }pool[lim << 2], *tail = pool, *root;
     
    Node * build(int l = 1, int r = lim){
        Node * nd = ++tail;
        if(l == r)nd -> now = G[l];
        else {
            int m = (l + r) >> 1;
            nd -> ls = build(l, m);
            nd -> rs = build(m+1, r);
            nd -> now = merge(nd -> ls -> now, nd -> rs ->now);
        }
        return nd;
    }
    #define Ls l, mid, nd -> ls
    #define Rs mid+1, r, nd -> rs
    Maxtri query(int L, int R, int l = 1, int r = lim, Node * nd = root){
        if(L <= l && R >= r)return nd->now;
     
        int mid = (l + r) >> 1;
        Maxtri ans;
        bool fg = 0;
        if(L <= mid){
            ans = query(L, R, Ls);
            fg = 1;
        }
        if(R > mid)
            if(fg) ans = merge(ans, query(L, R, Rs));
            else ans = query(L, R, Rs);
        return ans;
    }
    int main(){
        int cas, n, S;
        scanf("%d%d", &n, &S);
        for(int i = 1; i <= lim; i++)G[i].init();
        for(int i = 1; i <= n; i++){
            int p, u, v, w;
            scanf("%d%d%d%d", &p, &u, &v, &w);
            G[p].dis[u][v] = min(G[p].dis[u][v], w);
        }
        for(int t = 1;  t <= lim; t++)
        for(int k = 0; k < 9; k++)
            for(int i = 0; i < 9; i++)
                for(int j = 0; j < 9; j++)
                    G[t].dis[i][j] = min(G[t].dis[i][j], G[t].dis[i][k]+G[t].dis[k][j]);
        root = build();
        scanf("%d", &cas);
        while(cas--){
            int st, ed, siz;
            scanf("%d%d%d", &st, &ed, &siz);
            for(int i = 1; i <= siz; i++)
                scanf("%d%d", &q[i].l, &q[i].r);
            sort(q+1, q+1+siz, cmp);
            Maxtri ans = query(q[1].l, q[1].r);
            for(int i = 2; i <= siz; i++){
                ans = merge(ans, query(q[i].l, q[i].r));
            }
            ans.dis[st][ed] < inf ? printf("%d
    ", ans.dis[st][ed]) : puts("-1");
        }
     
    }
    View Code
  • 相关阅读:
    树上倍增求LCA(最近公共祖先)
    NOI题库分治算法刷题记录
    NOI题库刷题日志 (贪心篇题解)
    O(nlogn)LIS及LCS算法
    BLOG搬家
    『素数 Prime判定和线性欧拉筛法 The sieve of Euler』
    『扩展欧几里得算法 Extended Euclid』
    『NOIP2018普及组题解』
    P25、面试题1:赋值运算符函数
    字符串转成整数
  • 原文地址:https://www.cnblogs.com/EdSheeran/p/9275300.html
Copyright © 2020-2023  润新知