更新博客系列

好久不更新博客了……
来更新一发!
[bzoj4531]路径
dp[i][j][k][l] 表示这个东西有j个左括号,当前走了i步,上次在k节点,是不是单独的一个0.
然后就暴力分类大讨论即可。

#include <bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;++ i)
#define v edge[i].to
using namespace std;
int n,m,K;
bool w[30][30];
typedef long long LL;
const int mod = (int)1e9 + 7;
LL f[32][32][32][2];
char s[44];
bool dig(int x){return x >= '0' && x <= '9';}
void Add(LL &x,LL y){x = x + y;if(x >= mod)x %= mod;}
void dp()
{
    for(int i = 1;i <= n;++ i)
        if(s[i] == '0')f[1][0][i][1] = 1;
        else if(s[i] > '0' && s[i] <= '9')f[1][0][i][0] = 1;
        else if(s[i] == '-')f[1][0][i][0] = 1;
        else if(s[i] == '(')f[1][1][i][0] = 1;
    //Init
    for(int i = 1;i < K;++ i)
    {
        for(int j = 0;j <= i;++ j) //j:未匹配的括号数 *****************
        {
            for(int k = 1;k <= n;++ k)
            {
                for(int p = 1;p <= n;++ p) //枚举k的前驱 
                {
                    if(!w[k][p])continue;
                    if(dig(s[p]))
                    {
                        if(dig(s[k]))Add(f[i + 1][j][p][0],f[i][j][k][0]);//4.1
                        else if(s[k] != ')')Add(f[i + 1][j][p][s[p] == '0'],f[i][j][k][0]);//3.1
                        //if(s[p] == '0' && !dig(s[k]) && s[k] != ')')Add(f[i + 1][j][p][1],f[i][j][k][0]);
                    }
                    else if(s[p] == '*' || s[p] == '+' || s[p] == '-' || s[p] == '/')
                    {
                        if(dig(s[k]))//4.2
                        {
                            Add(f[i + 1][j][p][0],f[i][j][k][0]);
                            Add(f[i + 1][j][p][0],f[i][j][k][1]);
                        }
                        else if(s[k] == '(' && s[p] == '-')Add(f[i + 1][j][p][0],f[i][j][k][0]);//1.1
                        else if(s[k] == ')')Add(f[i + 1][j][p][0],f[i][j][k][0]);//2.1
                    }
                    else if(s[p] == '(')
                    {
                        if(dig(s[k]));
                        else if(s[k] == ')');
                        else Add(f[i + 1][j + 1][p][0],f[i][j][k][0]);//1.2 + 3.2
                    }
                    else if(s[p] == ')' && j > 0)
                    {
                        if(s[k] == '(' || s[k] == '*' || s[k] == '+' || s[k] == '-' || s[k] == '/');//如果s[k]不合法 
                        else if(s[k] == ')')Add(f[i + 1][j - 1][p][0],f[i][j][k][0]);//2.2
                        else Add(f[i + 1][j - 1][p][0],f[i][j][k][0]),Add(f[i + 1][j - 1][p][0],f[i][j][k][1]);//4.3(dig)
                    }
                }
            }
        }
    }
}
LL sum()
{
    LL cur = 0;
    Rep(i,n)if(dig(s[i]) || s[i] == ')')Add(cur,f[K][0][i][0]),Add(cur,f[K][0][i][1]);
    return cur;
}
int main()
{
    scanf("%d%d%d",&n,&m,&K);
    scanf("%s",s + 1);
    while(m --){int a,b;scanf("%d%d",&a,&b);w[a][b] = w[b][a] = 1;}
    dp();
    printf("%lld\n",sum());
    return 0;
}

bzoj4184: shallot
考虑一个事情就是,线性基是很难删除的。
那么我们可以用线段树分治来做这个事情。
我们用一棵时间线段树来表示每个节点的存在时间。
可以注意到的是,每个节点必然存在于连续的一段中。
我们把对应位置的区间打上永久化的标记,表示[l,r]这段区间中,这个节点是存在的。
这样的话,我们对于叶子节点[a,a],只需要考虑它到根路径上的所有节点上的标记即可知道[a,a]时刻的答案。
那么我们对线段树进行一次dfs,在dfs的过程中维护极简线性基即可。
总的复杂度是 O(nlognlogW)
维护只有插入操作的极简线性基?
增量法:如果我们已经维护了 {a1,a2,a3...,ak} 这样一组极简线性基,现在有一个新的基可以加入,那么我们要新加入一个东西作为新的基。
考虑到加入这个基之后,实际上低位的线性基中最高位的1并不一定是唯一的了,这时我们就用低位的先消掉这个基的1的位。
这样,这个基能消的就都被消掉了,我们用这个基去消更高位的基即可。
最后把所有的线性基异或起来就是答案。

#include<bits/stdc++.h>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define mid (l + r >> 1)
#define lson x << 1,l,mid
#define rson x << 1 | 1,mid + 1,r
using namespace std;
int m,n,ql,qr,cur_val;
const int N = 500001;
set<pair<int,int> >s;
vector<int>vec[N << 2];
struct Node
{
    int a[32];
    int &operator[](int x)
    {
        return a[x];
    }
}c;
void Ins(int x,Node &cur)
{
    int pos = -1;
    for(int i = 30;~ i;i --)
    {
        if(!cur[i] && x & (1 << i)){cur[i] = x;pos = i;break;}
        else if(x & (1 << i))x ^= cur[i];
    }
    if(~pos)
    {
        for(int i = pos - 1;~ i;i --)
        {
            if((cur[pos] & (1 << i)) && cur[i])
                cur[pos] ^= cur[i];
        }
        for(int i = 30;i != pos;-- i)
        {
            if(cur[i] & (1 << pos))
            {
                cur[i] ^= cur[pos];
            }
        }
    }
}
void Modify(int x,int l,int r)
{
    if(ql <= l && r <= qr)
    {
        vec[x].push_back(cur_val);
        return ;
    }
    if(ql <= mid)Modify(lson);
    if(mid < qr)Modify(rson);
}
void solve(int x,int l,int r,Node cur)
{
    int sz = vec[x].size();
    for(int i = 0;i < sz;++ i)Ins(vec[x][i],cur);
    if(l == r)
    {
        int val = 0;
        for(int i = 30;~ i;i --)val ^= cur[i];
        printf("%d\n",val);
        return ;
    }
    solve(lson,cur),solve(rson,cur);
}
int main ()
{
    scanf("%d",&n);
    Rep(i,n)
    {
        int x;
        scanf("%d",&x);
        if(x > 0)s.insert(make_pair(x,i));
        else
        {
            x = -x;
            pair<int,int> q = *s.lower_bound(make_pair(x,0));
            ql = q.second,qr = i - 1;
            cur_val = x;
            Modify(1,1,n);
            s.erase(q);
        }
    }
    for(set<pair<int,int> >:: iterator it = s.begin();it != s.end();++ it)
    {
        ql = (*it).second,qr = n,cur_val = (*it).first;
        Modify(1,1,n);
    }
    solve(1,1,n,c);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值