好久不更新博客了……
来更新一发!
[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(nlogn∗logW)
维护只有插入操作的极简线性基?
增量法:如果我们已经维护了{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;
}