【考试总结】2022-03-06
高维游走
首先使用 库默尔定理 发现将 划分成 才能在组合数中构成奇数
最朴素的想法是直接 std::bitset
来对每个 计算奇偶性,但是可以通过划分阶段来将问题简化
逐二进制位转移是个不错的选择,设 表示已经转移了前 位, 的值,转移枚举第 位这个 被哪个 选走了,就可以让 产生更新
这时如果将 分组,那么 ,也就是说奇偶性的变化只会在组内发生
那么由于已经转移的权值不超过 每组非零的 的数量不会超过 ,即每个组里面的元素不超过 个,又根据权值是 ,可以使用二进制位压缩
本质上 表示不相同的组是 数量级的,所以每个余数都开个变量非常蠢,再开桶 表示压缩后是 的组的数量
对于 不包含的位,从 发生的变化就是商是奇数的和商是偶数的分开了,这样子的变化可以预处理
对于 包含的位需要先枚举 同样包含这位的 把方案数变成偶数的异或掉再转移即可
Code Display
const int N=2000010;
int t[N],n,m,dp[2][N],ev[N],od[N];
signed main(){
freopen("travel.in","r",stdin); freopen("travel.out","w",stdout);
for(int i=1;i<=N-10;++i){
ev[i]=(ev[i>>2]<<1)|((i>>1)&1);
od[i]=(od[i>>2]<<1)|(i&1);
}
int T=read(); while(T--){
n=read(); rep(i,0,n) t[i]=read();
int cur=0,S=(1<<(n+1)); --S;
dp[cur][1]=1;
for(int i=0;i<31;++i){
if(t[0]>>i&1){
for(int st=0;st<=S;++st) if(dp[cur][st]){
int nxt=st;
for(int j=1;j<=n;++j) if(t[j]>>i&1) nxt^=(st<<j);
dp[cur^1][ev[nxt]]+=dp[cur][st];
dp[cur^1][od[nxt]]+=dp[cur][st];
dp[cur][st]=0;
}
}else{
for(int st=0;st<=S;++st) if(dp[cur][st]){
dp[cur^1][ev[st]]+=dp[cur][st];
dp[cur^1][od[st]]+=dp[cur][st];
dp[cur][st]=0;
}
}
cur^=1;
}
int ans=0;
for(int i=1;i<=S;++i) ans+=dp[cur][i]*__builtin_popcount(i),dp[cur][i]=0;
print(ans);
}
return 0;
}
过山车
如果只判定合法性的做法就是将每个点拆成横向/竖向两个点并再拉一个本原点出来
黑白染色之后本原点向源点/汇点连流量为 的边,同时向横向、竖向两个点都连流量为 的边
根据网格图将各个点的横向/纵向点连起来看看最大流是不是点数即可
如果附加权值考虑 “减少竖直边” 这样的求解方式,从本原点连向横向/纵向点流量为 边中一个带 权,另一个带 的权值即可
由于是最小费用最大流,那么一定先选择两个 权边,也就是拐弯的方法
Code Display
const int N=1e5+10,inf=0x3f3f3f3f;
struct edge{int to,nxt,lim,cst;}e[N<<2];
int head[N],dst[N],pre[N],incf[N];
int tot,id1[200][200],id[200][200],id2[200][200],S,T;
int n,m,cnt=1,ban[200][200],val[200][200];
bool inq[N];
inline bool spfa(){
for(int i=1;i<=tot;++i) incf[i]=0,dst[i]=inf; dst[S]=0; incf[S]=inf;
queue<int> q; q.push(S);
while(q.size()){
int fr=q.front(); q.pop(); inq[fr]=0;
for(int i=head[fr];i;i=e[i].nxt) if(e[i].lim){
int t=e[i].to; if(dst[fr]+e[i].cst<dst[t]){
dst[t]=dst[fr]+e[i].cst; incf[t]=min(incf[fr],e[i].lim); pre[t]=i;
if(!inq[t]) inq[t]=1,q.push(t);
}
}
}
return dst[T]!=inf;
}
inline void adde(int u,int v,int w,int c){
e[++cnt]={v,head[u],w,c}; head[u]=cnt;
return ;
}
inline void add(int u,int v,int w,int c){return adde(u,v,w,c),adde(v,u,0,-c);}
signed main(){
freopen("roller.in","r",stdin); freopen("roller.out","w",stdout);
n=read(); m=read();
int sum=0,block=0;
S=++tot; T=++tot;
rep(i,1,n) rep(j,1,m) ban[i][j]=read();
rep(i,1,n) rep(j,1,m){
val[i][j]=read();
sum+=(!ban[i][j])*val[i][j];
block+=!ban[i][j];
if(!ban[i][j]){
id1[i][j]=++tot,id2[i][j]=++tot,id[i][j]=++tot;
if((i+j)&1){
add(S,id[i][j],2,0);
add(id[i][j],id1[i][j],1,0);
add(id[i][j],id1[i][j],1,val[i][j]);
add(id[i][j],id2[i][j],1,0);
add(id[i][j],id2[i][j],1,val[i][j]);
}else{
add(id[i][j],T,2,0);
add(id1[i][j],id[i][j],1,0);
add(id1[i][j],id[i][j],1,val[i][j]);
add(id2[i][j],id[i][j],1,0);
add(id2[i][j],id[i][j],1,val[i][j]);
}
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j) if(!ban[i][j]&&(i+j)%2){
if(i+1<=n&&!ban[i+1][j]) add(id1[i][j],id1[i+1][j],1,0);
if(i-1&&!ban[i-1][j]) add(id1[i][j],id1[i-1][j],1,0);
if(j+1<=m&&!ban[i][j+1]) add(id2[i][j],id2[i][j+1],1,0);
if(j-1&&!ban[i][j-1]) add(id2[i][j],id2[i][j-1],1,0);
}
}
int flow=0;
while(spfa()){
int x=T;
while(x!=S){
e[pre[x]].lim-=incf[T];
e[pre[x]^1].lim+=incf[T];
x=e[pre[x]^1].to;
}
flow+=incf[T]; sum-=incf[T]*dst[T];
}
if(flow==block) print(sum);
else print(-1);
return 0;
}
木棍
将线段转化成左端点(也就是让限制的右端点 ),同时记 表示前 个整点上左端点数量
那么线段长度是
对于 条限制中也就是 ; 个木棍的位置可以表达成 ,最后根据实际含义还还有
先考察 ,设 表示区间 里面的线段数量,使用 定理发现 的限制需要表示为
较大时这样子的连边方式接受不了,尝试只保留 “限制区间” 的左端点 和右端点作为 关键点 及关键点所涉及到的边(也就是导出子图)来进行差分约束
对于原图上的一个不全是关键点的正环,找到一个非关键点,它在环上的连边不能是 条限制中的边,如果有连续的 或者 边,合并起来并不会让环的权值减小
那么保持 不变,让这个点所对应的区间缩小一定会让环上的权值不降,所以可以只对导出子图做差分约束
在进行差分约束的过程中,观察边的构成,容易使用 来处理
对于 这是经典的扫描线问题,在我的 chkmax
式差分约束中可以表示为
第四种 可以将 按照模 的余数排序之后,使用 维护后缀最大值即可
注意更新是有顺序的,在正序扫描的时候对于同余数的先都插入再查询,而倒序时要先都查询再插入,来避免漏转移或者错误的转移
如果把线段树换成 zkw 线段树
之后感觉没有常数优化余地了,不知道 是怎么做的,只能把正环长度卡到 了
Code Display
const int N=4010,inf=0x3f3f3f3f;
int n,m,k,x[N],y[N],c[N],a[N],b[N];
int num,ar[N];
vector<int> inter[N];
int now[N],nxt[N];
struct Fenwick{
int c[N];
inline void clear(){memset(c,-0x3f,sizeof(c));}
inline void insert(int x,int v){
for(;x;x-=x&(-x)) ckmax(c[x],v);
return ;
}
inline int query(int x){
int Mx=0;
for(;x<=num;x+=x&(-x)) ckmax(Mx,c[x]);
return Mx;
}
}T;
struct Seg{
#define ls p<<1
#define rs p<<1|1
int Mx[N<<2],tag[N<<2],bit;
inline void push_up(int p){Mx[p]=max(Mx[ls],Mx[rs])+tag[p];}
inline void build(int n){
bit=1; while(bit<=n+1) bit<<=1;
for(int i=bit+1;i<=bit+n;++i) Mx[i]=now[i-bit],tag[i]=0;
for(int i=bit-1;i>=1;--i) Mx[i]=max(Mx[i<<1],Mx[i<<1|1]),tag[i]=0;
return ;
}
inline void upd(int st,int ed){
st+=bit-1; ed+=bit+1;
while(st!=ed-1){
if(!(st&1)) Mx[st^1]++,tag[st^1]++;
if(ed&1) Mx[ed^1]++,tag[ed^1]++;
push_up(st>>=1); push_up(ed>>=1);
}
while(st>>1) push_up(st>>=1);
return ;
}
inline int query(int st,int ed){
st+=bit-1; ed+=bit+1;
int lef=0,rig=0;
while(st!=ed-1){
if(!(st&1)) ckmax(lef,Mx[st^1]);
if(ed&1) ckmax(rig,Mx[ed^1]);
lef+=tag[st>>=1]; rig+=tag[ed>>=1];
}
int Mx=max(lef,rig);
while(st>>1) Mx+=tag[st>>=1];
return Mx;
}
#undef ls
#undef rs
}seg;
int quo[N],rem[N],id[N];
signed main(){
freopen("stick.in","r",stdin); freopen("stick.out","w",stdout);
n=read(); m=read(); k=read();
rep(i,1,n){
a[i]=read(),b[i]=read()-k;
ar[++num]=a[i]-1; ar[++num]=b[i];
}
rep(i,1,m){
x[i]=read(),y[i]=read()-k,c[i]=read();
ar[++num]=x[i]-1; ar[++num]=y[i];
}
sort(ar+1,ar+num+1); num=unique(ar+1,ar+num+1)-ar-1;
rep(i,1,num){
quo[i]=ar[i]/k,rem[i]=(ar[i]%k+k)%k,id[i]=i;
while(k*quo[i]>ar[i]) quo[i]--;
}
sort(id+1,id+num+1,[&](const int &x,const int &y){return rem[x]<rem[y];});
rep(i,1,n){
a[i]=lower_bound(ar+1,ar+num+1,a[i]-1)-ar;
b[i]=lower_bound(ar+1,ar+num+1,b[i])-ar;
inter[b[i]].push_back(a[i]);
}
rep(i,1,m){
x[i]=lower_bound(ar+1,ar+num+1,x[i]-1)-ar;
y[i]=lower_bound(ar+1,ar+num+1,y[i])-ar;
}
//four kinds of edges
// first -> s[x[i]-1]>=s[y[i]]+c;
// second-> s[i]<=s[i+1]
// third -> s[i]>=s[j]-(i-j+k-1)/k
// fourth-> scaning line
memset(now,-0x3f,sizeof(now)); now[0]=0;
memset(nxt,-0x3f,sizeof(nxt));
for(int turn=1;turn<=num;++turn){
nxt[0]=now[0];
for(int i=1;i<=num;++i) nxt[i]=max(nxt[i-1],now[i]);
//second type
for(int i=1;i<=m;++i) ckmax(nxt[x[i]],now[y[i]]-c[i]);
//first type
seg.build(num);
for(int i=1;i<=num;++i){
for(auto t:inter[i]) seg.upd(1,t);
if(i-1) ckmax(nxt[i],seg.query(1,i-1));
}
//scaning line
T.clear();
for(int i=1;i<=num;++i){
int rig=i;
while(rig<num&&rem[id[rig+1]]==rem[id[i]]) ++rig;
for(int j=i;j<=rig;++j) T.insert(id[j],now[id[j]]-quo[id[j]]);
for(int j=i;j<=rig;++j) ckmax(nxt[id[j]],T.query(id[j]+1)+quo[id[j]]);
i=rig;
}
T.clear();
for(int i=num;i>=1;--i){
int rig=i;
while(rig>1&&rem[id[rig-1]]==rem[id[i]]) --rig;
for(int j=i;j>=rig;--j) ckmax(nxt[id[j]],T.query(id[j]+1)+quo[id[j]]-1);
for(int j=i;j>=rig;--j) T.insert(id[j],now[id[j]]-quo[id[j]]);
i=rig;
}
//third type
bool updd=0;
for(int i=1;i<=num;++i) if(now[i]!=nxt[i]){updd=1; break;}
if(!updd) break;
if(turn==num) puts("No"),exit(0);
for(int i=1;i<=num;++i) now[i]=nxt[i],nxt[i]=-inf;
}
puts("Yes");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律