Wannafly挑战赛17

A - 走格子

思路:
             直接按题意模拟即可。

#include<bits/stdc++.h>
typedef long long ll;
const int INF = 1e9 + 10;
const int maxn = 1e5 + 10;
using namespace std;

int n, m, T, kase = 1;
int a[maxn];

int dx[] = {1, 0, -1, 0};
int dy[] = {0, -1, 0, 1};
int vis[1010][1010];

int main() {
    while(scanf("%d %d", &n, &m) != EOF) {
        int x = 1, y = 1, st = 0;
        memset(vis, 0, sizeof vis);
        vis[1][1] = 1;
        while(m--) {
            while(1) {
                int nx = x + dx[st];
                int ny = y + dy[st];
                if(nx < 1 || nx > n || ny < 1 || ny > n || vis[nx][ny]) {
                    st = (st + 1) % 4;
                } else break;
            }
            x = x + dx[st];
            y = y + dy[st];
            vis[x][y] = 1;
        }
        printf("%d %d\n", x, y);
    }
    return 0;
}


B - 求值

思路:
             有个公式 k(nk)(mxk)=(n+mx) ∑ k ( n k ) ( m x − k ) = ( n + m x ) ,然后 (nk)=(nnk) ( n k ) = ( n n − k ) ,所以 v(iv)2=v(iv)(iiv)=(2ii) ∑ v ( i v ) 2 = ∑ v ( i v ) ( i i − v ) ① = ( 2 i i ) ,然后预处理一下阶乘还有逆元就好了。我得方法是①式是卷积, 用了 NTT N T T 。。。

#include<bits/stdc++.h>
typedef long long ll;
const int mod = 998244353;
const int maxn = 2e6 + 1e5;
using namespace std;
int fac, inv;
int a[maxn], b[maxn], rev[maxn];
int qmod(int x, int n, int mod) {
    int ans = 1;
    while(n) {
        if(n & 1) ans = (ll)ans * x % mod;
        x = (ll)x * x % mod;
        n >>= 1;
    }
    return ans;
}
void NTT(int *a, int len, int op) {
    for(int i = 0; i < len; i++) if(i < rev[i]) swap(a[i], a[rev[i]]);
    for(int i = 1; i < len; i <<= 1) {
        int wn = qmod(3, ((mod - 1) / i / 2 * op + mod - 1) % (mod - 1), mod);
        ///Complex wn(cos(pi / i), op * sin(pi / i));
        int step = i << 1;
        for(int j = 0; j < len; j += step) {
            int w = 1, x, y;
            for(int k = 0; k < i; k++, w = ((ll)w * wn) % mod) {
                x = a[j + k]; y = ((ll)w * a[j + k + i]) % mod;
                a[j + k] = (x + y) % mod; a[j + k + i] = ((x - y) % mod + mod) % mod;
            }
        }
    }
}

void init() {
    fac = inv = 1;
    a[0] = b[0] = 1;
    for(int i = 1; i < 1e6 + 2; i++) {
        fac = (ll)fac * i % mod;
        fac = (ll)fac * i % mod;
    //if(i < 10) cout << "i = " << i << "  fac = " << fac << endl;
        inv = qmod(fac, mod - 2, mod);
        a[i] = b[i] = inv;
    }
  //  cout << (ll)(qmod(3, mod-2,mod) + 1) * (36) % mod  << endl;
    int len = 1, l = 0, L = 1e6 + 3;
    while(len <= L + L) { len <<= 1; l++; }
    //cout << len << endl;
    for(int i = 0; i < len; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
    NTT(a, len, 1);
    for(int i = 0; i < len; i++) a[i] = (ll)a[i] * a[i] % mod;
    NTT(a, len, -1); inv = qmod(len, mod - 2, mod);
    for(int i = 0; i < 1000010; i++) a[i] = (ll)a[i] * inv % mod;
   // cout << a[3] << endl;
    rev[0] = 0; fac = 1;
    for(int i = 1; i < 1000010; i++) {
        fac = (ll)fac * i % mod;
        fac = (ll)fac * i % mod;

        rev[i] = (rev[i - 1] + (ll)fac * a[i]) % mod;

       // if(i < 10) cout << "fac = " << fac << "  rev" << rev[i] << endl;
    }
}
int main() {
    init();
    int n; while(scanf("%d", &n) != EOF) printf("%d\n", rev[n]);
    return 0;
}


C - 简单环

思路:
             dp[S][i] d p [ S ] [ i ] :现在的一条链的集合是 S S 这个状态且结束点是i开始点是 S S 中标号最小的点a的方案数,那么对于下一条链,如果 i,j(j>a) i , j ( j > a ) 有边且 j j 没有在S中,那么 dp[S|1<<j][j] += dp[S][i] d p [ S | 1 << j ] [ j ]   + =   d p [ S ] [ i ] ,那么如果 a,k a , k 也有边那就形成了一个环,从小到大枚举状态即可,每个环会重复计算两次,答案除以2就行了。

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 1e5 + 10;
const ll mod = 998244353;
using namespace std;

int vis[25][25];
ll dp[1 << 20][20], tot[1 << 20], ans[20];

int main() {
    ios::sync_with_stdio(0);
    int n, m, k;
    ll inv = (mod + 1) / 2;
    while(cin >> n >> m >> k) {
        memset(vis, 0, sizeof vis);
        memset(dp, 0, sizeof dp);
        memset(tot, 0, sizeof tot);
        memset(ans, 0, sizeof ans);
        for(int i = 0; i < m; i++) {
            int x, y; cin >> x >> y;
            x--; y--;
            vis[x][y] = vis[y][x] = 1;
        }
        for(int i = 0; i < n; i++) dp[1 << i][i] = 1;
        int max_sta = 1 << n;
        for(int i = 0; i < max_sta; i++) {
            int min_point = -1;
            for(int j = 0; j < n; j++) if(((i >> j) & 1)) {
                if(min_point == -1) min_point = j;  ///固定以标号最小的点为链的开始点
                for(int k = min_point + 1; k < n; k++) {
                    if((i >> k) & 1) continue;
                    if(!vis[j][k]) continue;
                    int next_sta = i | (1 << k);
                    (dp[next_sta][k] += dp[i][j]) %= mod;
                    if(vis[min_point][k]) (tot[next_sta] += dp[i][j]) %= mod;
                }
            }
            int tx = __builtin_popcount(i);
            if(tx >= 3) (ans[tx % k] += tot[i] * inv) %= mod; ///环计算了两次
        }
        for(int i = 0; i < k; i++) cout << ans[i] << endl;
    }
    return 0;
}


D - 01序列

思路:
             对于一个只有 01 01 的区间,设长度为 l l ,其值表示为al1al2...a0,如果要模 3 3 0,则有 (2l1al1+2l2al2+..+20a0) mod 3=0=((1)l1al1+(1)l2al2+..+(1)0a0) ( 2 l − 1 ⋅ a l − 1 + 2 l − 2 ⋅ a l − 2 + . . + 2 0 ⋅ a 0 )   m o d   3 = 0 = ( ( − 1 ) l − 1 ⋅ a l − 1 + ( − 1 ) l − 2 ⋅ a l − 2 + . . + ( − 1 ) 0 ⋅ a 0 ) ,当 ai a i 等于 0 0 时没有影响,所以只要保证这个区间在奇数位置上的1和偶数位置上的 1 1 相差为3的倍数即模 3 3 0,那么这个值就是 3 3 的倍数,线段树维护每个区间3的倍数的区间数量 sum s u m ,节点对应区间以左端点开始和以右端点开始相差值 d mod 3=0,1,2 d   m o d   3 = 0 , 1 , 2 的区间数量 d1,d2 d 1 , d 2 ,还有该区间的 d0 d 0 值,合并的时候 sum=suml+sumr+mid s u m = s u m l + s u m r + 跨 越 m i d 的 区 间 数 量 ,跨越 mid m i d 的是左孩子余数 x x d2右孩子余数 y y d1( x+y mod  3=0 x + y   m o d     3 = 0 ), d0 d 0 直接相加即可, d1,d2 d 1 , d 2 根据两个孩子的 d0 d 0 值再加上去就行。查询的时候类似分治,求完全包含在 [l,mid],[mid+1,r] [ l , m i d ] , [ m i d + 1 , r ] 的,递归解决就行,还有跨越 mid m i d 的,也是类似上面的去维护。

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 5e5 + 10;
using namespace std;

struct seg {
    ll l[3], r[3], sum, rec;
} C[maxn * 4];
int n, m, op, x, y;
int bit[maxn];

void push_up(int o) {
    int o1 = o << 1, o2 = o << 1 | 1;
    for(int i = 0; i < 3; i++) {
        C[o].l[i] = C[o1].l[i];
        C[o].r[i] = C[o2].r[i];
        int ls = (i - C[o1].rec), rs = i - C[o2].rec;
        if(ls < 0) ls += 3;
        if(rs < 0) rs += 3;
        C[o].l[i] += C[o2].l[ls];
        C[o].r[i] += C[o1].r[rs];
    }
    C[o].sum = C[o1].sum + C[o2].sum;
    for(int i = 0; i < 3; i++) {
        C[o].sum += C[o1].r[i] * C[o2].l[(3 - i) % 3];
    }
    C[o].rec = (C[o1].rec + C[o2].rec) % 3;
}

void build(int o, int l, int r) {
    for(int i = 0; i < 3; i++) {
        C[o].l[i] = C[o].r[i] = C[o].sum = C[o].rec = 0;
    }
    if(l == r) {
        for(int i = 0; i < 3; i++) C[o].l[i] = C[o].r[i] = C[o].sum = 0;

        int ds = 0;
        if(bit[l] == 0) ds = 0;
        else if(l & 1) ds = 1;
        else ds = 2;
        C[o].l[ds] = C[o].r[ds] = 1;
        C[o].rec = ds;
        if(ds == 0) C[o].sum = 1;
        else C[o].sum = 0;

        return ;
    }
    int mid = (l + r) >> 1;
    build(o << 1, l, mid);
    build(o << 1 | 1, mid + 1, r);
    push_up(o);

}

void update(int o, int l, int r, int ind) {
    if(l == r) {
        for(int i = 0; i < 3; i++) C[o].l[i] = C[o].r[i] = C[o].sum = 0;
        bit[l] ^= 1;
        int ds = 0;
        if(bit[l] == 0) ds = 0;
        else if(l & 1) ds = 1;
        else ds = 2;
        C[o].l[ds] = C[o].r[ds] = 1;
        if(ds == 0) C[o].sum = 1;
        else C[o].sum = 0;
        C[o].rec = ds;
        return ;
    }
    int mid = (l + r) >> 1;
    if(ind <= mid) update(o << 1, l, mid, ind);
    else update(o << 1 | 1, mid + 1, r, ind);
    push_up(o);
}

seg query(int o, int l, int r, int ql, int qr) {
    if(l > qr || r < ql) {
        seg s;
        for(int i = 0; i < 3; i++) s.l[i] = s.r[i] = s.sum = s.rec = 0;
        return s;
    }
    if(l >= ql && r <= qr) return C[o];
    int mid = (l + r) >> 1;
    seg ans;
    seg p1 = query(o << 1, l, mid, ql, qr);
    seg p2 = query(o << 1 | 1, mid + 1, r, ql, qr);
    ans.rec = (p1.rec + p2.rec) % 3;
    ans.sum = p1.sum + p2.sum;
    for(int i = 0; i < 3; i++) {
        ans.l[i] = p1.l[i];
        ans.r[i] = p2.r[i];
        int ls = (i - p1.rec), rs = (i - p2.rec);
        if(ls < 0) ls += 3; if(rs < 0) rs += 3;
        ans.l[i] += p2.l[ls];
        ans.r[i] += p1.r[rs];
    }
    for(int i = 0; i < 3; i++) ans.sum += p1.r[i] * p2.l[(3 - i) % 3];
    return ans;
}
int main() {
    while(scanf("%d %d", &n, &m) != EOF) {
        for(int i = 1; i <= n; i++) scanf("%d", &bit[i]);
        build(1, 1, n);
        while(m--) {
            scanf("%d", &op);
            if(op == 1) {
                scanf("%d", &x);
                update(1, 1, n, x);
            } else {
                scanf("%d %d", &x, &y);
                ll ans = query(1, 1, n, x, y).sum;
                printf("%lld\n", ans);
            }
        }
    }
    return 0;
}


E - 跳格子

思路:
             因为往回跳的时候只能跳到已经跳的格子上去,那么如果往回跳的时候结束点 sum s u m 跳到了 y y 相当于sum到了 y y 这个位置需要跳回去的方案数,那么设dp[x]:结束点在 x x 这个格子时跳回1之前方案数,设 ti t i 表示一次最多跳 n n 步的要跳i步长的方案数,那么 dp[x]=dp[x1]t1+dp[x2]t2+....+dp[xm]tm d p [ x ] = d p [ x − 1 ] ⋅ t 1 + d p [ x − 2 ] ⋅ t 2 + . . . . + d p [ x − m ] ⋅ t m ,记忆化搜索即可,注意他是从 0 0 跳到sum然后跳回 0 0 <script type="math/tex" id="MathJax-Element-8386">0</script>。

#include<bits/stdc++.h>
typedef long long ll;
const ll mod = 233333333;
const int maxn = 4e5 + 10;
using namespace std;

ll dp[maxn], tot[20];

void init(int n) {
    tot[0] = 1;
    for(int i = 1; i < 20; i++) {
        for(int j = 1; j <= n; j++) {
            if(i - j < 0) continue;
            tot[i] = (tot[i] + tot[i - j]) % mod;
        }
    }
}

ll dfs(int now, int n, int m) {
    if(now < 1) return 0;
    if(now == 1) return dp[now] = 1;
    ll &res = dp[now];
    if(res != -1) return res;
    res = 0;
    for(int i = 1; i <= m; i++) {
        //if(i > n) break;
        //res += dfs(now - i, n, m) * tot[i] % mod;
        ll kk = dfs(now - i, n, m);
        //printf("dp[%d] += %lld(dp[%d]) * %lld(tot[%d])\n", now, kk, now - i, tot[i], i);
        res += kk * tot[i] % mod;
        if(res >= mod) res -= mod;
    }
    return res;
}

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        int s, n, m;
        memset(dp, -1, sizeof dp);
        scanf("%d %d %d", &s, &n, &m);
        s++; memset(tot, 0, sizeof tot);
        init(n);
        //for(int i = 1; i < 10; i++) printf("tot[%d] = %lld\n", i, tot[i]);
        ll ans = dfs(s, n, m);
        //for(int i = 1; i <= s; i++) printf("dp[%d] = %lld\n", i, dp[i]);
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值