T1:
题意:
给出
m
m
m个值在
[
0
,
2
32
)
[0,2^{32})
[0,232)内的数,
k
k
k次操作添加一个数或删除一个数,或询问某个
x
x
x是否能被这些数异或得到。
m
,
k
≤
1
0
5
m,k\le10^5
m,k≤105
题解:
线性基套个线段树分治即可。网上好像有基于线性基性质的神奇做法,不过复杂度似乎都是两个log。
Code:
#include<bits/stdc++.h>
#define maxn 100005
#define ui unsigned int
using namespace std;
int n,m,k;
ui d[32],a[maxn],all;
char s[35];
map<ui,int>pre;
vector<ui>q[maxn<<2];
inline ui turn(char *s){
ui x=0;
for(int i=0;s[i];i++) x=x<<1|(s[i]-'0');
return x;
}
inline bool ins(ui *d,ui x){
for(int i=n-1;i>=0;i--) if(x>>i&1){
if(!d[i]) return d[i]=x,1;
x^=d[i];
}
return 0;
}
void add(int i,int l,int r,int x,int y,ui v){
if(x<=l&&r<=y) {q[i].push_back(v);return;}
int mid=(l+r)>>1;
if(x<=mid) add(i<<1,l,mid,x,y,v);
if(y>mid) add(i<<1|1,mid+1,r,x,y,v);
}
void solve(int i,int l,int r,const ui *d){
ui t[32]; memcpy(t,d,sizeof t);
for(int j=q[i].size()-1;j>=0;j--) ins(t,q[i][j]);
if(l==r) {puts(ins(t,all^a[l])?"NO":"YES");return;}
int mid=(l+r)>>1;
solve(i<<1,l,mid,t),solve(i<<1|1,mid+1,r,t);
}
int main()
{
freopen("lamp.in","r",stdin);
freopen("lamp.out","w",stdout);
int op; ui x;
scanf("%d%d%d",&n,&m,&k),all=(1ll<<n)-1,k++;
scanf("%s",s),a[1]=turn(s);
for(int i=1;i<=m;i++) scanf("%s",s),pre[turn(s)]=1;
for(int i=2;i<=k;i++){
scanf("%d%s",&op,s),x=turn(s);
if(!op) a[i]=x;
else {a[i]=a[i-1]; if(pre[x]) add(1,1,k,pre[x],i-1,x),pre[x]=0; else pre[x]=i;}
}
for(map<ui,int>::iterator it=pre.begin();it!=pre.end();it++)
if(it->second) add(1,1,k,it->second,k,it->first);
solve(1,1,k,d);
}
T2
题意:
n
n
n个点
m
m
m条边的有向图,求1走到n再走到1的最短路径长度,可以反转一条边,反转的代价是
D
i
D_i
Di。
n
≤
200
,
m
≤
50000
n\le200,m\le50000
n≤200,m≤50000
题解:
考试的时候重边距离没有取 min \min min爆零了。。
考虑1到n的最短路,(n到1同理),如果反转一条边
(
x
,
y
)
(x,y)
(x,y)带来的影响是有两个:原来的最短路可能断掉了;新产生了
1
→
y
→
x
→
n
1\to y\to x\to n
1→y→x→n的路径。
我们建出最短路树。
对于第一个影响,如果断掉的边不在
1
1
1到
n
n
n的树边上(条件可以放宽为不在树边上,不影响复杂度),那么最短路不变。
对于第二个影响,如果直接用原来的
d
i
s
[
1
]
[
y
]
+
w
(
x
,
y
)
+
d
i
s
[
x
]
[
n
]
dis[1][y]+w(x,y)+dis[x][n]
dis[1][y]+w(x,y)+dis[x][n]有可能不合法,即
d
i
s
[
1
]
[
y
]
dis[1][y]
dis[1][y]经过了
(
x
,
y
)
(x,y)
(x,y)这条边,相当于要求不经过
(
x
,
y
)
(x,y)
(x,y)从
1
1
1到
y
y
y的最短路,如果
(
x
,
y
)
(x,y)
(x,y)不是
1
→
y
1\to y
1→y的树边,那么就是合法的。
对于在
(
x
,
y
)
(x,y)
(x,y)在树边上的情况,由于只有
O
(
n
)
O(n)
O(n)条,重新建图跑
O
(
n
2
)
O(n^2)
O(n2)的Dijkstra即可。
复杂度
O
(
n
3
+
n
m
)
O(n^3+nm)
O(n3+nm)。
Code:
#include<bits/stdc++.h>
#define maxn 205
#define maxm 50005
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m,ans,dis[maxn][maxn],X[maxm],Y[maxm],C[maxm],D[maxm];
int fir[maxn],nxt[maxm],to[maxm],cst[maxm],tot;
inline void line(int x,int y,int z){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,cst[tot]=z;}
struct Graph{
int s,t,pre[maxn],w[maxn][maxn],d[maxn];
bool vis[maxn],mark[maxm];
int Dijkstra(int K){
memset(w,0x3f,sizeof w),memset(vis,0,sizeof vis),memset(d,0x3f,sizeof d);
for(int i=1;i<=m;i++)
if(i!=K) w[X[i]][Y[i]]=min(w[X[i]][Y[i]],C[i]);
else w[Y[i]][X[i]]=min(w[Y[i]][X[i]],C[i]);
d[s]=0;
for(int o=1;o<n;o++){
int k=0;
for(int i=1;i<=n;i++) if(!vis[i]&&d[i]<d[k]) k=i;
vis[k]=1;
for(int i=1;i<=n;i++) if(!vis[i]) d[i]=min(d[i],d[k]+w[k][i]);
}
return d[t];
}
void dfs(int u){
for(int i=fir[u],v;i;i=nxt[i])
if(dis[s][u]+cst[i]==dis[s][v=to[i]]&&!pre[v])
pre[v]=u,mark[i]=1,dfs(v);
}
int solve(int k){
if(!mark[k]) return min(dis[s][Y[k]]+C[k]+dis[X[k]][t],dis[s][t]);
else return Dijkstra(k);
}
}G1,Gn;
int main()
{
freopen("lane.in","r",stdin);
freopen("lane.out","w",stdout);
scanf("%d%d",&n,&m);
memset(dis,0x3f,sizeof dis);
for(int i=1;i<=n;i++) dis[i][i]=0;
for(int i=1;i<=m;i++) scanf("%d%d%d%d",&X[i],&Y[i],&C[i],&D[i]),dis[X[i]][Y[i]]=min(dis[X[i]][Y[i]],C[i]),line(X[i],Y[i],C[i]);
for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
ans=dis[1][n]+dis[n][1];
G1.s=Gn.t=1,G1.t=Gn.s=n;
G1.pre[1]=1,G1.dfs(1),Gn.pre[n]=n,Gn.dfs(n);
for(int i=1;i<=m;i++){
int x=G1.solve(i),y=Gn.solve(i);
ans=min(ans,min(x+y,inf)+D[i]);
}
printf("%d\n",ans<inf?ans:-1);
}
T3:
题意:
有
n
n
n个嵌套起来的for循环,循环上界为
m
m
m。对于第
i
i
i个循环有两个参数
x
i
(
<
i
)
x_i(<i)
xi(<i)和
c
i
c_i
ci,如果
c
i
≠
0
c_i\neq 0
ci=0,那么它写作
f
o
r
(
a
i
=
c
i
;
a
i
≤
m
;
a
i
+
+
)
for(a_i=c_i;a_i\le m;a_i++)
for(ai=ci;ai≤m;ai++),否则,它写作
f
o
r
(
a
i
=
a
x
i
;
a
i
≤
m
;
a
i
+
+
)
for(a_i=a_{x_i};a_i\le m;a_i++)
for(ai=axi;ai≤m;ai++)
n
≤
4000
,
m
≤
1
0
18
n\le4000,m\le10^{18}
n≤4000,m≤1018
题解:
稍加思考可以发现
f
o
r
for
for循环的依赖关系形成了森林,对于一个
c
i
≠
0
c_i\neq0
ci=0的循环就是一棵树的根,树与树之间是独立的可以相乘。
对于一棵树可以跑一个简单的树形DP,设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示
i
i
i号循环还剩
j
j
j次到达
m
m
m时子树中的循环次数的总和。那么
f
[
u
]
[
i
]
=
∑
j
=
1
i
∏
f
[
v
]
[
j
]
f[u][i]=\sum_{j=1}^i\prod f[v][j]
f[u][i]=∑j=1i∏f[v][j]。复杂度
O
(
n
m
)
O(nm)
O(nm)。
Code(写这道题的时候意识到拉格朗日插值求幂和在
n
≤
m
o
d
n\le mod
n≤mod时是可以做到
O
(
k
l
o
g
k
)
O(klogk)
O(klogk)的,但是当
n
n
n过大时分子就不一定有逆元了):
#include<bits/stdc++.h>
#define maxn 4005
#define LL long long
using namespace std;
const int mod = 998244353;
int n,D,siz[maxn],inv[maxn],f[maxn][maxn];
int fir[maxn],nxt[maxn],to[maxn],tot;
LL m,c[maxn];
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
void dfs(int u){siz[u]=1;for(int i=fir[u];i;i=nxt[i]) dfs(to[i]),siz[u]+=siz[to[i]];}
void solve(int u){
fill(f[u]+1,f[u]+D+1,1);
for(int i=fir[u],v;i;i=nxt[i]){
solve(v=to[i]);
for(int j=1;j<=D;j++) f[u][j]=1ll*f[u][j]*f[v][j]%mod;
}
for(int i=2;i<=D;i++) f[u][i]=(f[u][i]+f[u][i-1])%mod;
}
int main()
{
freopen("tower.in","r",stdin);
freopen("tower.out","w",stdout);
scanf("%d%lld",&n,&m);
inv[0]=inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=1,x;i<=n;i++) scanf("%d%lld",&x,&c[i]),!c[i]&&(line(x,i),0);
int ans=1;
for(int i=1;i<=n;i++) if(!siz[i]){
dfs(i),D=siz[i]+1,solve(i);
int X=(m-c[i]+1)%mod,ret=0;
for(int j=1;j<=D;j++){
int Y=f[i][j];
for(int k=1;k<=D;k++) if(j!=k) Y=1ll*Y*(X-k)%mod*(j>k?inv[j-k]:-inv[k-j])%mod;
ret=(ret+Y)%mod;
}
ans=1ll*ans*ret%mod;
}
printf("%d\n",(ans+mod)%mod);
}