zoukankan      html  css  js  c++  java
  • 2019.9.14 校内交流测试

    2019中秋佳节 欢乐赛

    T1  残缺棋盘(chessboard.cpp)

     【题目描述】

    大佬LDX和FQH一动不动地在棋盘前已经沉默地坐了五个小时。他们全神贯注地盯着每粒棋子。

    突然,LDX说:“原则上我是反对在下棋时说话的,但是我现在不得不开口问:现在究竟该谁走下一步棋了?”

    FQH说:“谁先走都不重要了,现在的问题是:谁把这个棋盘上的格子损坏了?”

    正如图所示,有一正方形棋盘,其边长为2k(1<k<10),其中有一格损坏。现在想用如图中间所示形状的硬纸板将没有坏的所有格子盖起来。而硬纸板不得放入坏格中和棋盘外面。编程输出一种覆盖方案,将共用一块硬纸板的三个格子用相同的数字表示。

    上图所示是k=2的情形,且输出结果不一定和图示方案一致,符合题目要求即可,输出时只需输出数字方阵而不必画出格子线。

    【输入格式】

    三个整数,即k和坏格子的y坐标和x坐标(注意坏格子的坐标输入顺序)。

    【输出格式】

    数字方阵,其中坏坐标以数字7表示。

    【输入样例】

    2 1 1

    【输出样例】

    7 4 2 2

    4 4 4 2

    3 4 4 4

    3 3 4 4

    题解

    因为边长是 2k  ,所以,一定可以把矩形划分成一个个 2*2 的小方块

    在2*2的方块中,也就是k=1时,就很容易确定数字的填法了

    那么k>1呢???

    分治

    中心块开始补 

    然后补全就是这样:

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<string>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    
    using namespace std;
    
    typedef long long ll;
    
    inline int read()
    {
        int ans=0;
        char last=' ',ch=getchar();
        while(ch<'0'||ch>'9') last=ch,ch=getchar();
        while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar();
        if(last=='-') ans=-ans;
        return ans;
    }
    
    int k,x,y;
    int pop[15]={1,2,4,8,16,32,64,128,256,512,1024};
    int ans[2000][2000];
    
    
    int check(int lx,int ly,int rx,int ry,int gx,int gy)
    {
        if(gx==lx&&gy==ly) return 1;
        if(gx==lx&&gy==ry) return 2;
        if(gx==rx&&gy==ly) return 3;
        if(gx==rx&&gy==ry) return 4;
    }
    
    
    void tian(int lx,int ly,int rx,int ry,int gx,int gy)
    {
        int p=check(lx,ly,rx,ry,gx,gy);
        if(p==1){
            ans[rx][ly]=5-p;
            ans[lx][ry]=5-p;
            ans[rx][ry]=5-p;
        }
        if(p==2){
            ans[lx][ly]=5-p;
            ans[rx][ly]=5-p;
            ans[rx][ry]=5-p;
        }
        if(p==3){
            ans[lx][ly]=5-p;
            ans[lx][ry]=5-p;
            ans[rx][ry]=5-p;
        }
        if(p==4){
            ans[lx][ly]=5-p;
            ans[lx][ry]=5-p;
            ans[rx][ly]=5-p;
        }
    }
    
    void dfs(int lx,int ly,int rx,int ry,int gx,int gy)
    {
        if(ry-ly+1==2) {
            tian(lx, ly, rx, ry, gx, gy);
            return ;
        }
    
        
        int lon=(ry-ly+1)/2;
        int ax=lx+lon-1,ay=ly+lon-1,
            bx=ax+1,by=ay+1;
    
        if(gx<=ax&&gy<=ay){
           dfs(lx,ly,ax,ay,gx,gy);
        dfs(lx,by,ax,ry,ax,by);
        dfs(bx,ly,rx,ay,bx,ay);
        dfs(bx,by,rx,ry,bx,by);
        }
        if(gx>ax&&gy<=ay){
            dfs(lx,ly,ax,ay,ax,ay);
        dfs(lx,by,ax,ry,ax,by);
        dfs(bx,ly,rx,ay,gx,gy);
        dfs(bx,by,rx,ry,bx,by);
        }
        if(gx<=ax&&gy>ay){
            dfs(lx,ly,ax,ay,ax,ay);
        dfs(lx,by,ax,ry,gx,gy);
        dfs(bx,ly,rx,ay,bx,ay);
        dfs(bx,by,rx,ry,bx,by);
        }
        if(gx>ax&&gy>ay){
        dfs(lx,ly,ax,ay,ax,ay);
        dfs(lx,by,ax,ry,ax,by);
        dfs(bx,ly,rx,ay,bx,ay);
        dfs(bx,by,rx,ry,gx,gy);
        }
        int m=gx<=ax?ax:bx;
        int n=gy<=ay?ay:by;
        tian(ax,ay,bx,by,m,n);
        return ;
    }
    
    
    int main()
    {
        freopen("chessboard.in","r",stdin);
        freopen("chessboard.out","w",stdout);
        k=read();y=read();x=read();
        ans[x][y]=7;
    
        dfs(1,1,pop[k],pop[k],x,y);
        
        for(int i=1;i<=pop[k];i++){
            for(int j=1;j<=pop[k];j++)
              printf("%d ",ans[i][j]);
            printf("
    ");
        }
        
        return 0;
    }
    
    
     

     T2   (ac.cpp)

    AC自动机:

    题目背景

    YZD在每天学习20小时的勤奋研究下,终于开发出了AC自动机!但是,这台AC自动机有一些bug,比如两个部件之间经常会出一些莫名其妙的问题,所以他想要随时知道被损坏的两个部件之间有多少根电线.

    题目描述

    AC自动机的结构是一个有着n个节点,2n - 2条边的有向图,

    前n-1条边,从部件1连向部件2-n,形成了一颗生成树。

    后n-1条边,从部件2-n依次连向部件1.

    你需要支持两种操作:查询两个点的最短路和修改一条边的长度。

    输入输出格式

    输入格式

    第一行两个整数n,q,为点的数目和询问的数目;

    下面2n-2行,每行三个整数u, v, w, 表示从u到v连一条有向边。

    下面q行,每行3个整数k,u,v,若k=1,询问从u到v的最短路, 若k=2,修改第u条边长度为v。

    输出格式

    对于1询问, 每行输出一个整数,表示最短路的长度

    样例

    输入

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

    3 1 2
    4 1 3
    5 1 4
    1 3 2
    2 3 -3
    1 3 2

    输出

    5
    3

    范围

    对于40 %的数据,没有2操作。

    对于100 %的数据, n,q <= 1e5.

    题解

    spfa TLE 

    堆优化的dij TLE 

    FLoyd 不仅TLE还WA

    所以,树链剖分!

    下面我们看着代码来讲一讲

    首先存边 然后存完了

    边分两种

          1~n-1 条:以1为根的一棵生成树

    n~2n-2 条:点2~n依次直接连回点1

    我们会得到一颗生成树,对于这棵生成树,

    每一个节点都是由序号比它小的节点所指向,也就是这句话的含义:

           >

    每一个节点都有一条直连边返回节点1

           >

    然后我们对这棵树dfs

    一颗子树对应dfs序上的一个区间

    对于每个点 x 我们记录两个数值 L[x] , r[x]

    L[x] 表示 x 的 dfs 序(用dfn表示)(它是第几个被遍历到的)

    r[x] 表示回溯到 x 时已经搜了几个点

    那么 L[x]和r[x]也就构成了一个区间(同一棵子树的dfs序是连续的)

    以x为根的子树构成区间的左dfn和右dfn

    所以一颗子树对应dfs序上的一个区间

    然后我们以dfs序建树

    t[k].min = data[x]+f[x]

    t[k].data = data[x] 只有k代表的区间是单点,也就是树上一个叶节点的时候记录data

    对于修改操作分两种:

    1.修改n~2n-2的边:

       此时只会影响到一条边,也就是线段树单点修改

       arg传进去标记为1

       线段树修改也就是只需要修改 min 和 tag2

    2.修改1~n-1的边:

       此时会影响到 以 修改边的出点 为根的子树中所有点的 dep 值

       也就是区间 [ L[x]  ,  r[x] ]中所有点的 dep 值会受影响

       arg默认标记0

       modify(1,L[x],r[x],修改量)

    对于查询呢?

    get操作是将y跳到与x同一深度

    如果此时y=x,那么 x 到 y 的路径有 x 一直往下走,走到 y 

    不然的话 x 到 y 可以分两种情况:

    (1)x 跳到 根节点 ,根节点跳到 y 

    (2)x 跳到自己的子节点,子节点跳到根,根跳到 y 

     

     综合分析也就是,x 先跳到自己子树内的一个点,然后从这个点跳到根节点,再从根节点跳到 y,路径取最小值

     也就是  query(1,L[x],r[x])- pos(1,L[x])+ pos(1,L[y])

    代码

    #include <cstdio>
    #include <cctype>
    #include <vector>
    #define ll long long
    #include <algorithm>
    const ll maxn = 200005;
    const ll inf = 1e13;
    
    struct edge{
        ll from, to, w;
        edge(ll c, ll a, ll b):from(c), to(a),w(b){}
        edge(){}
    }e[maxn << 1];
    
    ll n, q, cnt, dfn, ans;
    ll fa[maxn][20];
    ll f[maxn],  //点 i 到 点 1 的距离 ,回去 
       dep[maxn], //点 1 到 点 i 的距离 ,去 
       deep[maxn], //点i的深度 
       l[maxn], //子树k的左儿子编号 
       que[maxn], //dfn序列 
       r[maxn]; //子树k的右儿子编号 
    
    std::vector<ll> to[maxn];  //to[u] u的出边 编号 
    
    
    
    void add_edge(ll u, ll v, ll w) {
        e[++cnt] = edge(u, v, w);
        to[u].push_back(cnt);
    }
    
    
    
    ll read() {
        ll x = 0, f = 1;
        char ch = getchar();
        while(!isdigit(ch)) {
        if(ch == '-') f = -1;
        ch = getchar();
        }
        while(isdigit(ch)) {
        x = x * 10 + ch - '0';
        ch = getchar();
        }
        return x * f;
    }
    
    
    
    struct seg{
        ll l, r, 
           min, //min(dep)+f 
           dat, // 
           tag1, tag2; //tag1:dep, tag2:f
    }t[maxn<<2];
    
    
    
    void update(ll k) {
        t[k].min = std::min(t[k<<1].min, t[k<<1|1].min);
    }
    
    
    
    void pushdown(ll k) {
        if(t[k].l == t[k].r) return;
        
        if(t[k << 1].l == t[k<<1].r) t[k<<1].dat += t[k].tag1;
        
        if(t[k << 1|1].l == t[k<<1|1].r) t[k<<1|1].dat += t[k].tag1;
        
        t[k<<1].min += t[k].tag2 + t[k].tag1;
        t[k<<1|1].min += t[k].tag2 + t[k].tag1;
        
        t[k<<1].tag1 += t[k].tag1;
        t[k<<1].tag2 += t[k].tag2;
        
        t[k<<1|1].tag1 += t[k].tag1;
        t[k<<1|1].tag2 += t[k].tag2;
        
        t[k].tag1 = t[k].tag2 = 0;
    }
    
    
    
    void build(ll k, ll l, ll r) {
        t[k].l = l, t[k].r = r, t[k].tag1 = t[k].tag2 = 0;
        if(l == r) {
        t[k].min = dep[que[l]] + f[que[l]];
        t[k].dat = dep[que[l]];
        return;
        }
        ll mid = (l + r) >> 1;
        build(k << 1, l, mid);
        build(k << 1|1, mid+1, r);
        update(k);
    }
    
    
    
    void modify(ll k, ll x, ll y, ll a, ll arg = 0) {
    //arg 可以写也可以不写,如果不写就默认0 
        pushdown(k);
        ll l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
        
        if(x <= l && r <= y) {
          if(arg == 0) {
            t[k].tag1 += a;
            t[k].min += a;
            if(t[k].l == t[k].r) t[k].dat += a;
          }
          else {
            t[k].tag2 += a;
            t[k].min += a;
          }
          return;
        }
        
        if(x <= mid) modify(k << 1, x, y, a, arg);
        if(y > mid) modify(k << 1|1, x, y, a, arg);
        
        update(k);
    }
    
    ll pos(ll k, ll p) 
    //节点1-->节点 dfs序 为p的节点的距离 
    {
        pushdown(k);
        ll l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
        if(l == r) return t[k].dat;
        if(p <= mid) return pos(k << 1, p);
        else return pos(k << 1|1, p);
    }
    
    ll query(ll k, ll x, ll y) 
    //节点1-->区间[x,y]中,点的最短距离 
    {
        pushdown(k);
        ll l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
        if(x <= l && r <= y) return t[k].min;
        ll ans = inf;
        if(x <= mid) ans = std::min(ans, query(k << 1, x, y));
        if(y > mid) ans = std::min(ans, query(k << 1|1, x, y));
        return ans;
    }
    
    
    void dfs(ll x, ll fa) {
    //    一个子树对应dfs序上的一个子区间
        l[x] = ++dfn;
        
        que[dfn] = x;
        
        for(ll i = 0; i < to[x].size(); i++) {
          ll v = e[to[x][i]].to;
          if(v != fa) {
            dep[v] = dep[x] + e[to[x][i]].w;
            deep[v] = deep[x] + 1;
            dfs(v, x);
          }
        }
        
        r[x] = dfn;
    }
    
    
    
    void pre() {
        scanf("%lld %lld", &n, &q);
        
        for(ll i = 1; i < n; i++) { //生成树连边,存父亲 
        ll u = read(), v = read();
        add_edge(u, v, read());
        fa[v][0] = u;
        }
        
        for(ll i = 1; i < n; i++) { //连边,回去 
        ll u = read(), v = read();
        f[u] = read();
        e[++cnt] = edge(u, v, f[u]);
        }
        
        for(ll i = 1; i <= 18; i++)
          for(ll j = 1; j <= n; j++)
            fa[j][i] = fa[fa[j][i-1]][i-1]; 
        //处理每个点的祖先 
            
        dfs(1, 0);
        
        build(1, 1, dfn);
    }
    
    
    
    ll get(ll x, ll d) {
        ll D = deep[x];
        ll p = D - d;
        if(p < 0) return -1; //y比x浅 
        ll u = x;
        for(ll i = 18; i >= 0 && p; i--) {
        if((1<<i) <= p) {
            p -= (1<<i);
            u = fa[u][i]; //u往上跳 
        }
        }
        return u;
    }
    
    void solve() {
       while(q--) {
        ll k = read(), x = read(), y = read();
        if(k == 2) {
            if(x > (n-1)) {
            modify(1, l[e[x].from], l[e[x].from], y - f[e[x].from], 1);
            f[e[x].from] = y;
            }
            else {
            ll delta = y - e[x].w;
            ll u = e[x].to;
            modify(1, l[u], r[u], delta);
            e[x].w = y;
            }
        }
        else {
            ll k = get(y, deep[x]);
            if(k == x) ans = pos(1, l[y]) - pos(1, l[x]);
            else ans = query(1, l[x], r[x]) - pos(1, l[x]) + pos(1, l[y]);
            printf("%lld
    ", ans);
        }
      }
    }
    
    int main() {
    #ifdef orz
        freopen("input", "r", stdin);
    #endif
        pre();
        solve();
    }

    T3   自动AK(ak.cpp)

    题目描述

    YZD继AC自动机之后,为了更好地A题,他每天学习172309651385613894032916519835028936513785613278490623578136502364983271570863209847928356113785600123984173256134850129466329041623665837012934872130647132805612398472316507613257802471926347213561029830348937721958612302381943821735213504982137412381965123785913260948723981067132865023908472319808565231785012149823147013256193284721096421387561029047123094632578012364890273412350612935623057个小时,在半个月的83279419851635195618290384782396517895620498723046132705652198325632785613204974591235127398023473056130975613750729351761029843732890651785601293847208356103765239018471853610934823747413456021983497239506137056192834729031651780562384759820981347187561780479812657864501293874920651027364912875天里,终于研发出了自动AK机!

    但是,YZD的自动AK机目前还只有核心部分,还需要完善。其中,代码检查部分由于只涉及到字符串匹配,YZD不屑于写那么简单的代码。

    于是,他把这任务交给了你:

    你需要完成三个任务:

    任务1:代码可以以一个长为的只含小写字母的字符串组成;现在给定一个长为的模板字符串,也只含有小写字母。现在,你需要求出所有模板串在代码串中出现的位置。

    任务2:同任务1,但由于这次YZD需要了解更多信息,你需要求出模板串从代码串的每个位置开始最长出现的多少(就是说,对于代码串的每个位置,你需要求出模板串最长的前缀,使得这个前缀在代码串的这个位置开始出现了)。

    任务3:同任务1,但是由于数据缺失,模板字符串里可能会出现’?’字符,它可以看做任何字符(每个‘?’只能看做恰好一个字符)。你需要求出模板串在代码串里所有可能出现的位置。

    输入输出格式

    输入格式

    输入第一行一个数字,表示你要完成哪个任务。

    接下来两行分别是代码串和模板串。

    输出格式

    对于任务1、3,你要输出一行两个数,分别表示出现(或可能出现)了多少次和所有出现的开始位置的异或值。位置从0开始编号。

    对于任务2, 你要输出一行4个数a0,a1,a2,a3,ai表示模4余i的所有位置的答案的异或值。位置从0开始。

    样例

    输入一

    1
    aabaabaaa
    aabaa

    输出一

    2 3

    输入二

    2
    aabaabaaa
    aabaa

    输出二

    5 1 2 7

    输入三

    3
    aabaabaaa
    a??aa

    输出三

    3 7

    样例解释

    样例一,模板串在位置0、3出现;

    样例二,模板串在九个位置出现的最大长度分别为5,1,0,5,1,0,2,2,1。

    样例三,模板串在位置0,3,4出现。

    范围

    任务1, 2: 代码串和模板串长度.

    任务3: 代码串和模板串长度.

     

    题解

    任务一:kmp板子题

    任务二:简单来说,就是代码串从每个位置开始往后,作为代码串,和模式串进行匹配,问最大匹配的个数,对于每个位置,都会得到一个值,把这些值,按照 ' 位置 ' 分类,取异或和

    任务三: 没鼓捣明白

    神奇的 FFT

    求个卷积

    设代码串为A,模式串为B

    C(i , j)= (Ai - Bj2

    若C(i,j)=0证明  Ai , B匹配

    QAQ 没听明白 我错啦 耽误您时间了

    代码

    #include <algorithm>
    #include <cmath>
    #include <complex>
    #include <cstdio>
    #include <cstring>
    typedef long long LL;
    const int N = 1005000;
    char s[N], p[N];
    int n, m;
    int something[N];
    namespace Task1{
      int* const nxt = something;
      void getNxt() {
        nxt[0] = nxt[1] = 0;
        for (int i = 1, j = 0; i < m; ++i) {
          while (j && p[i] != p[j]) j = nxt[j];
          nxt[i + 1] = (p[i] == p[j] ? ++j : 0);
        }
      }
      void solve() {
        int ans1 = 0, ans2 = 0;
        getNxt();
        for (int i = 0, j = 0; i < n; ++i) {
          while (j && p[j] != s[i]) j = nxt[j];
          if (p[j] == s[i] && (++j == m)) {
            ++ans1;
            ans2 ^= i - m + 1;
          }
        }
        printf("%d %d
    ", ans1, ans2);
      }
    };
    namespace Task2{
      int *const f = something;
      void getF() {
        f[0] = m;
        int rmax = 0, ri = 0;
        for (int i = 1; i < m; ++i) {
          f[i] = std::max(0, std::min(f[i - ri], rmax - i));
          while (p[f[i]] == p[i + f[i]]) ++f[i];
          if (i + f[i] > rmax)
            rmax = i + f[ri = i];
        }
      }
      void solve() {
        int ansv[4] = {0, 0, 0, 0};
        getF();
        int rmax = 0, ri = 0;
        for (int i = 0; i < n; ++i) {
          int ans = std::max(0, std::min(rmax - i, f[i - ri]));
          while (i + ans < n && ans < m && s[i + ans] == p[ans]) ++ans;
          if (i + ans > rmax)
            rmax = (ri = i) + ans;
          ansv[i % 4] ^= ans;
        }
        printf("%d %d %d %d
    ", ansv[0], ansv[1], ansv[2], ansv[3]);
      }
    };
    namespace Task3{
      const int N = 400000;
      typedef std::complex<double> Complex;
      const double pi = acos(-1.0);
      Complex PA[N], PB[N];
      LL A[N], B[N], C[N];
      void FFT(Complex *P, int len, int opt) {
        for (int i = 1, j = len >> 1, k; i < len; ++i) {
          if (i < j) std::swap(P[i], P[j]);
          k = len;
          do j ^= (k >>= 1); while (~j & k);
        }
        for (int h = 2; h <= len; h <<= 1) {
          Complex wn = Complex(cos(2 * pi * opt / h), sin(2 * pi * opt / h));
          for (int j = 0; j < len; j += h) {
            Complex w = Complex(1.0, .0);
            for (int k = 0; k < h / 2; ++k) {
              Complex tmp = P[j + k], tmp2 = P[j + k + h / 2] * w;
              P[j + k] = tmp + tmp2;
              P[j + k + h / 2] = tmp - tmp2;
              w *= wn;
            }
          }
        }
        if (opt == -1) {
          for (int i = 0; i < len; ++i)
            P[i] /= len;
        }
      }
      void upd(LL *A, LL *B, LL *C, int n, int m) { //A += B[0..n-1] * C[0..m-1]
        int len = 1;
        while (len < n + m) len <<= 1;
        for (int i = 0; i < n; ++i) PA[i] = B[i];
        for (int i = n; i < len; ++i) PA[i] = .0;
        for (int i = 0; i < m; ++i) PB[i] = C[i];
        for (int i = m; i < len; ++i) PB[i] = .0;
        FFT(PA, len, 1);
        FFT(PB, len, 1);
        for (int i = 0; i < len; ++i)
          PA[i] *= PB[i];
        FFT(PA, len, -1);
        for (int i = 0; i < len; ++i)
          A[i] += (LL)(PA[i].real() + (PA[i].real() > 0 ? 0.5 : -0.5));
      }
      inline LL sqr(LL x) { return x * x; }
      inline LL cube(LL x) { return x * x * x; }
      void solve() {
        for (int i = 0; i < n; ++i)
          B[i] = sqr(s[i] - 'a' + 1);
        for (int i = 0; i < m; ++i)
          C[i] = (p[m - i - 1] == '?' ? 0 : p[m - i - 1] - 'a' + 1);
        upd(A, B, C, n, m);
        for (int i = 0; i < n; ++i)
          B[i] = -2 * (s[i] - 'a' + 1);
        for (int i = 0; i < m; ++i)
          C[i] = sqr(C[i]);
        upd(A, B, C, n, m);
        LL t = 0;
        for (int i = 0; i < m; ++i) if (p[i] != '?')
          t += cube(p[i] - 'a' + 1);
        int ans1 = 0, ans2 = 0;
        for (int i = 0; i <= n - m; ++i) if (A[i + m - 1] == -t) {
          ++ans1;
          ans2 ^= i;
        }
        printf("%d %d
    ", ans1, ans2);
      }
    }
    int main() {
      int task;
      scanf("%d%s%s", &task, s, p);
      n = strlen(s);
      m = strlen(p);
      if (task == 1) Task1::solve();
      if (task == 2) Task2::solve();
      if (task == 3) Task3::solve();
      return 0;
    }
  • 相关阅读:
    js 实现 间隙滚动效果
    js实现无缝滚动
    js 实现弹出层效果
    jQuery实现网页定位导航
    右侧定位的样式
    jQuery实现点击按钮展开和收起
    js实现点击按钮控制展开与收起.
    【BZOJ1202】[HNOI2005]狡猾的商人 带权并查集
    【BZOJ1067】[SCOI2007]降雨量 RMQ+特判
    【BZOJ1367】[Baltic2004]sequence 左偏树
  • 原文地址:https://www.cnblogs.com/xiaoyezi-wink/p/11523518.html
Copyright © 2011-2022 走看看