图论(?专项测试1
T1 序列
开题感觉用普通数据结构维护有(gen)点(ben)麻(bu)烦(hui),于是想了想,可以分块做。
在每个块内将元素从小到大排序,那么取 \(max\) 的操作会影响的只会是每个块左部的一个区间,可以打个差分标记维护修改次数。
做加法的操作比较好说,每个块上打完标记后,接下来的取 \(max\) 直接按这个标记的值偏移一下,就不用把加法标记真正打下去。
具体地,先给 \(a_i=0\) 加 \(2\) ,再让它与 \(5\) 取 \(max\) ,就可以看做 \(a_i=0\) 与 \(5-2=3\) 取 \(max\) ,最后查询时再加 \(2\) 。
所以加法可以相当于无视掉了。每次做零散操作时把 \(max\) 和差分标记下放就好。
粗略算了下,块长好像 \(\frac{\sqrt n}{2}\) 最优,但实测 \(\frac{\sqrt n}{3}\) 更快。( waitingcoders
上只能 \(\frac{\sqrt n}{3}\) 才能过
\(code:\)
T1
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL;
#define int LL
int read(){
int x=0,f=0; char ch=getchar();
while(ch>'9'||ch<'0'){ f|=(ch=='-'); ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char output[50];
void write(LL x,char sp){
int len=0;
if(x<0) putchar('-'), x=-x;
do{ output[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
}
} using namespace IO;
const int NN=100010;
int n,m,a[NN];
char op;
namespace Blocks{
const int BB=800;
struct node{
LL vl; int id;
node(){}
node(int t){ vl=t; }
bool operator<(const node& t)const{
return vl<t.vl;
}
}s[NN];
int blo,lp[BB],rp[BB],bel[NN],pos[NN];
LL tag[BB],gat[BB],dve[NN],mnv[BB],slf[NN];
void init(){
blo=sqrt(n)/2;
for(int i=1;i<=n;i++){
s[i].vl=a[i]; s[i].id=i;
bel[i]=(i-1)/blo+1;
if(!lp[bel[i]]) lp[bel[i]]=i;
if(!rp[bel[i]-1]) rp[bel[i]-1]=i-1;
}
rp[bel[n]]=n;
for(int i=1;i<=bel[n];i++){
sort(s+lp[i],s+rp[i]+1);
mnv[i]=s[lp[i]].vl;
}
for(int i=1;i<=n;i++) pos[s[i].id]=i;
}
void update(int id){
for(int sum=0,i=lp[id];i<=rp[id];i++){
s[i].vl=max(s[i].vl,mnv[id]);
sum+=dve[i]; dve[i]=0;
slf[s[i].id]+=sum;
}
}
void add(int l,int r,int v){
if(!v) return;
update(bel[l]); update(bel[r]);
for(int i=l;i<=min(r,rp[bel[l]]);i++) s[pos[i]].vl+=v, ++slf[i];
sort(s+lp[bel[l]],s+rp[bel[l]]+1); mnv[bel[l]]=s[lp[bel[l]]].vl;
for(int i=lp[bel[l]];i<=rp[bel[l]];i++) pos[s[i].id]=i;
if(bel[l]==bel[r]) return;
for(int i=lp[bel[r]];i<=r;i++) s[pos[i]].vl+=v, ++slf[i];
sort(s+lp[bel[r]],s+rp[bel[r]]+1); mnv[bel[r]]=s[lp[bel[r]]].vl;
for(int i=lp[bel[r]];i<=rp[bel[r]];i++) pos[s[i].id]=i;
for(int i=bel[l]+1;i<bel[r];i++)
tag[i]+=v, ++gat[i];
}
void mxn(int l,int r,int v){
update(bel[l]); update(bel[r]);
for(int i=l;i<=min(r,rp[bel[l]]);i++)
if(s[pos[i]].vl<v-tag[bel[l]]) s[pos[i]].vl=v-tag[bel[l]], ++slf[i];
sort(s+lp[bel[l]],s+rp[bel[l]]+1); mnv[bel[l]]=s[lp[bel[l]]].vl;
for(int i=lp[bel[l]];i<=rp[bel[l]];i++) pos[s[i].id]=i;
if(bel[l]==bel[r]) return;
for(int i=lp[bel[r]];i<=r;i++)
if(s[pos[i]].vl<v-tag[bel[r]]) s[pos[i]].vl=v-tag[bel[r]], ++slf[i];
sort(s+lp[bel[r]],s+rp[bel[r]]+1); mnv[bel[r]]=s[lp[bel[r]]].vl;
for(int i=lp[bel[r]];i<=rp[bel[r]];i++) pos[s[i].id]=i;
for(int i=bel[l]+1;i<bel[r];i++) if(mnv[i]<v-tag[i]){
int loc=lower_bound(s+lp[i],s+rp[i]+1,node(v-tag[i]))-s;
++dve[lp[i]]; mnv[i]=v-tag[i];
if(loc<=rp[i]) --dve[loc];
}
}
void query(int k){
update(bel[k]);
write(s[pos[k]].vl+tag[bel[k]],' ');
write(slf[k]+gat[bel[k]],'\n');
}
void solve(){
while(m--){
cin>>op;
if(op=='A'){
int l=read(),r=read(),c=read();
add(l,r,c);
} else if(op=='M'){
int l=read(),r=read(),c=read();
mxn(l,r,c);
} else query(read());
}
}
}
signed main(){
// freopen("seq.in","r",stdin);
// freopen("seq.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) a[i]=read();
Blocks::init();
m=read();
Blocks::solve();
return 0;
}
T2 旅行计划
以下默认 \(u,v\) 联通。
\(k\) 为奇数时,在 \(u,v\) 之间走 \(k\) 遍就能取到 \(0\) 。接下来考虑偶数 \(k\) 。
令 \(E\) 为 \(u,v\) 所在联通块的边集,设 \(d=gcd(k,gcd_{l\in E}\left\{w_l\right\})\) ,那么答案肯定为 \(d\) 的倍数。不妨暂时将所有 \(w_l\) 都除以 \(d\) ,最终计算时再乘回去。
此时 \(gcd(k,gcd_{l\in E}\left\{w_l\right\})=1\) ,根据裴蜀定理,存在集合 \(A\) ,使 \(\sum_{l\in E}A_lw_l\equiv 1(\mod k)\) 。对于一个联通图,我们可以通过遍历 \(DFS\) 树,遇到非树边时往返的方式构造一个每条边都出现两次的回路。所以不难调整出贡献为 \(2\) 的环。因此实际上答案只与奇偶有关。
设 \(d'=gcd_{l\in E}\left\{w_l\right\}\) ,那么当 \(\frac{d}{d'}\) 为偶数时,所有 \(\frac{w_l}{k}\) 都为偶数,因此肯定能调整出 \(0\) 。否则 \(\frac{w_l}{d}\) 与 \(\frac{w_l}{d'}\) 奇偶性相同,因此可以直接将边权预处理为 \(\frac{w_l}{d'}\) ,检查 \(u,v\) 所在联通块内是否存在奇环,或 \(u,v\) 间是否有长为偶数的路径。若有奇环,则可以通过奇环调整奇偶性,从而得到 \(0\) ;若有长为偶数的路径答案也应为 \(0\) ,否则就只能为 \(1\) ,即最终答案为 \(d\) 。
检查偶数路径可以直接在 \(DFS\) 树上染两种色,然后看两个点颜色有没有交集。
\(code:\)
T2
#include<bits/stdc++.h>
using namespace std;
namespace IO{
#define x first
#define y second
#define gcd __gcd
int read(){
int x=0,f=0; char ch=getchar();
while(ch>'9'||ch<'0'){ f|=(ch=='-'); ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char output[50];
void write(int x,char sp){
int len=0;
if(x<0) putchar('-'), x=-x;
do{ output[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
}
} using namespace IO;
const int NN=100010;
int n,m,q,dep[NN],col[NN];
vector<pair<int,int>>e[NN];
namespace DSU{
int ff[NN],gg[NN];
bool odd[NN];
int getf(int x){ return x==ff[x]?x:ff[x]=getf(ff[x]); }
void merge(int x,int y,int w){
x=getf(x); y=getf(y);
if(x==y) return;
if(gg[x]) gg[x]=gcd(gg[x],w);
else gg[x]=w;
if(gg[y]) gg[x]=gcd(gg[x],gg[y]);
ff[y]=x;
}
} using namespace DSU;
void dfs(int u,int d){
dep[u]=d;
for(auto& v:e[u]){
if(dep[v.x]>=0){
if(dep[u]-dep[v.x]+v.y&1) odd[getf(u)]=1;
continue;
}
dfs(v.x,d+v.y);
}
}
void Dfs(int u,int c){
if(col[u]&(1<<c)) return;
col[u]|=(1<<c);
for(auto& v:e[u]) Dfs(v.x,c^v.y);
}
signed main(){
// freopen("travel.in","r",stdin);
// freopen("travel.out","w",stdout);
n=read(); m=read(); q=read();
for(int i=1;i<=n;i++) ff[i]=i;
for(int u,v,w,i=1;i<=m;i++){
u=read(),v=read(),w=read(),merge(u,v,w);
e[u].push_back({v,w}); e[v].push_back({u,w});
}
for(int i=1;i<=n;i++)
for(auto& v:e[i])
v.y=v.y/gg[getf(i)]&1;
memset(dep,0xc0,sizeof(dep));
for(int i=1;i<=n;i++) if(dep[i]<0)
dfs(i,0), Dfs(i,0);
while(q--){
int u=read(),v=read(),k=read(),g=gcd(k,gg[getf(u)]);
if(getf(u)!=getf(v)){ puts("NIE"); continue; }
if(odd[getf(u)]||(col[u]&col[v])||!(gg[getf(u)]/g&1)){ puts("0"); continue; }
write(g,'\n');
}
return 0;
}
T3 hack
如果不考虑恰好一次的限制,就是求最小割。考虑怎么满足限制。
发现不满足限制的情况肯定是一些不相交路径之间存在横叉边。要使这个横叉边前后不同时存在割边,可以对每个边连一条流量为 \(\infty\) 的反边。
这样连边后要注意 \(S\) 不能到达的点或不能到达 \(T\) 的点不能连边,不然会改变原图的联通性而导致错误,如 \(0\to 2,2\to 1,1\to 0\) , \(0\to 2\) 本来是割边,连反边后却不再是了。
无解当且仅当 \(S\) 与 \(T\) 在一个强连通分量内。
\(code:\)
T3
#include<bits/stdc++.h>
using namespace std;
namespace IO{
#define int long long
int read(){
int x=0,f=0; char ch=getchar();
while(ch>'9'||ch<'0'){ f|=(ch=='-'); ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char output[50];
void write(int x,char sp){
int len=0;
if(x<0) putchar('-'), x=-x;
do{ output[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
}
} using namespace IO;
const int NN=110,MM=200010;
int n,m,uu[MM],vv[MM],ww[MM];
namespace Network_Flows{
int S,T,ql,qr,cut,q[NN],idx=1;
int c[MM],to[MM],nex[MM],dis[NN],thd[NN],head[NN];
void add(int a,int b,int x){
to[++idx]=b; nex[idx]=head[a]; head[a]=idx; c[idx]=x;
to[++idx]=a; nex[idx]=head[b]; head[b]=idx; c[idx]=0;
}
bool bfs(){
memset(dis,0x3f,sizeof(dis));
memcpy(head,thd,sizeof(thd));
dis[S]=0; q[ql=qr=1]=S;
while(ql<=qr){
int u=q[ql++];
for(int v,i=head[u];i;i=nex[i]) if(c[i])
if(dis[v=to[i]]>dis[u]+1){
dis[v]=dis[u]+1;
q[++qr]=v;
}
if(u==T) return 1;
}
return 0;
}
int dfs(int u,int in){
if(u==T) return in;
int rest=in,go;
for(int v,i=head[u];i;head[u]=i=nex[i]) if(c[i]){
if(dis[v=to[i]]==dis[u]+1){
go=dfs(v,min(rest,c[i]));
if(go) c[i]-=go, c[i^1]+=go, rest-=go;
else dis[v]=0;
}
if(!rest) break;
}
return in-rest;
}
void dinic(){
memcpy(thd,head,sizeof(head));
while(bfs()) cut+=dfs(S,LLONG_MAX);
}
} using namespace Network_Flows;
vector<int>e[NN],E[NN];
int top,cnt,tmp,num,scc[NN],stk[NN],low[NN],dfn[NN];
bool vis[NN];
void tarjan(int u){
vis[stk[++top]=u]=1; low[u]=dfn[u]=++cnt;
for(int v:e[u])
if(!dfn[v]) tarjan(v), low[u]=min(low[u],low[v]);
else if(vis[v]) low[u]=min(low[u],dfn[v]);
if(low[u]==dfn[u]){
++num;
do{ scc[tmp=stk[top--]]=num; vis[tmp]=0; }while(tmp!=u);
}
}
int col[NN];
void Dfs(int u,int t){
if(vis[u]) return;
vis[u]=1; col[u]|=t;
if(t==1) for(int v:e[u]) Dfs(v,t);
else for(int v:E[u]) Dfs(v,t);
}
signed main(){
n=read(); m=read(); T=n-1;
for(int w,i=1;i<=m;i++){
uu[i]=read(); vv[i]=read(); ww[i]=read();
e[uu[i]].push_back(vv[i]);
E[vv[i]].push_back(uu[i]);
}
tarjan(0);
Dfs(S,1); memset(vis,0,sizeof(vis)); Dfs(T,2);
if(scc[0]==scc[n-1]) return puts("-1"),0;
for(int i=1;i<=m;i++) if(col[uu[i]]==3&&col[vv[i]]==3)
add(uu[i],vv[i],ww[i]), add(vv[i],uu[i],LLONG_MAX);
dinic();
write(cut,'\n');
return 0;
}