首页 > 技术文章 > 沈阳集训day3

EdSheeran 2018-07-09 16:30 原文

问题 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("\n");   
        }
        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\n", ans.dis[st][ed]) : puts("-1");
    }
 
}
View Code

 

推荐阅读