专项测试1

A. 序列

写的在线分块做法 复杂度 \(O(n\sqrt{n}\log{n})\)

对每个块都先排序,散块先 \(pushdown\) 再暴力处理,最后排序重构

区间赋最大值的整块操作,可以二分出来一个操作的位置,前面的都需要修改

于是可以差分一下,在 \(pushdown\) 的时候还原再修改回去

赛时的写法常数飞起,随机数据甚至没跑过暴力就弃掉了,赛后换了个常数小点的就过了

Code
#include<bits/stdc++.h>
#define int long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m;
int cc[100010],mx[100010];
int bl[100010],blo;
char st[10];
struct node{
	int v,c,id;
	inline bool operator<(const node &b)const{return v<b.v;}
}aa[100010];
struct BLOCK{
	int atag,ctag;
	int l,r,lstp,lstv;
	inline void pushdown(){
		for(int i=l+1;i<=r;i++) cc[i]+=cc[i-1];
		for(int i=r-1;i>=l;i--) mx[i]=max(mx[i],mx[i+1]);
		for(int i=l;i<=r;i++){
			if(mx[i]!=-inf) aa[i].v=mx[i];
			aa[i].v+=atag,aa[i].c+=ctag,aa[i].c+=cc[i];
		}
	}
	inline void rebuild(){
		atag=0,ctag=0;lstp=l;lstv=-inf;
		for(int i=l;i<=r;i++) cc[i]=0,mx[i]=-inf;
		sort(aa+l,aa+r+1);
	}
	inline void getmx(int k){
		k-=atag;
		if(k<=lstv) return;
		int pos=upper_bound(aa+lstp,aa+r+1,(node){k-1,0,0})-aa-1;
		if(pos>=lstp){
			lstp=pos;cc[l]++;
			if(pos<r) cc[pos+1]--;
			mx[pos]=max(mx[pos],k);
			lstv=k;
		}
	}
}B[510];
inline void upd(int l,int r,int k){
	if(bl[l]==bl[r]){
		B[bl[l]].pushdown();
		for(int i=B[bl[l]].l;i<=B[bl[l]].r;i++) if(aa[i].id>=l&&aa[i].id<=r) aa[i].v+=k,aa[i].c++;
		B[bl[l]].rebuild();
		return ;
	}
	B[bl[l]].pushdown();B[bl[r]].pushdown();
	for(int i=B[bl[l]].l;i<=B[bl[l]].r;i++) if(aa[i].id>=l&&aa[i].id<=r) aa[i].v+=k,aa[i].c++;
	for(int i=B[bl[r]].l;i<=B[bl[r]].r;i++) if(aa[i].id>=l&&aa[i].id<=r) aa[i].v+=k,aa[i].c++;
	for(int i=bl[l]+1;i<bl[r];i++) B[i].atag+=k,B[i].ctag++;
	B[bl[l]].rebuild();B[bl[r]].rebuild();
}
inline void getmx(int l,int r,int k){
	if(bl[l]==bl[r]){
		B[bl[l]].pushdown();
		for(int i=B[bl[l]].l;i<=B[bl[l]].r;i++) if(aa[i].id>=l&&aa[i].id<=r) if(aa[i].v<k) aa[i].v=k,aa[i].c++;
		B[bl[l]].rebuild();
		return ;
	}
	B[bl[l]].pushdown();B[bl[r]].pushdown();
	for(int i=B[bl[l]].l;i<=B[bl[l]].r;i++) if(aa[i].id>=l&&aa[i].id<=r) if(aa[i].v<k) aa[i].v=k,aa[i].c++;
	for(int i=B[bl[r]].l;i<=B[bl[r]].r;i++) if(aa[i].id>=l&&aa[i].id<=r) if(aa[i].v<k) aa[i].v=k,aa[i].c++;
	for(int i=bl[l]+1;i<bl[r];i++) B[i].getmx(k);
	B[bl[l]].rebuild();B[bl[r]].rebuild();
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	n=read();blo=200;
	for(int i=1;i<=n;i++) aa[i].v=read(),aa[i].id=i;
	for(int i=1;i<=n;i++) bl[i]=(i-1)/blo+1;
	for(int i=1;i<=bl[n];i++) B[i].l=(i-1)*blo+1,B[i].r=min(n,i*blo);
	for(int i=1;i<=bl[n];i++) B[i].rebuild();
	m=read();
	for(int i=1,l,r,k;i<=m;i++){
		scanf("%s",st+1);
		if(st[1]=='A'){l=read(),r=read(),k=read();if(k) upd(l,r,k);}
		if(st[1]=='M'){l=read(),r=read(),k=read();getmx(l,r,k);}
		if(st[1]=='Q'){k=read();B[bl[k]].pushdown();for(int i=B[bl[k]].l;i<=B[bl[k]].r;i++) if(aa[i].id==k) printf("%lld %lld\n",aa[i].v,aa[i].c);B[bl[k]].rebuild();}
	}
	return 0;
}

B. 旅行计划

如果 \(u,v\) 不在一个块里直接输出 \(\text{NIE}\)

发现最后的答案一定为 \(gcd(K,l_1,l_2,...)\) 其中 \(l\) 为边的边权

简单意会一下 一堆数加加减减再模一模他们的 \(gcd\) 不变

所以可以将所有边权先除一个 \(gcd\) 最后再乘回来

如果 \(K\) 为奇数的话直接输出 \(0\) 你可以在 \((u,v)\) 之间来回走 \(K\) 次使得答案为 \(0\)

那么只用考虑偶数的情况

根据裴蜀定理一定存在一种解使得 \(k_1l_1+k_2l_2+...+k_KK=1\)

然后在模 \(K\) 意义下 一定存在 \(k_1l_1+k_2l_2+...+k_nl_n\equiv 1 \mod{K}\)

然后你可以构造出来一种走法使得每条边都出现 \(2\)

\(dfs\) 树上走,遇见非树边就上去再下来,剩下的树肯定能只走 \(2\)

需要哪一条边多走就循环走几次

所以可以构造出来一种走法使得这个走法的贡献为 \(2\)

然后只需要判断 \((u,v)\) 之间是否存在长度为偶数的路径如果存在答案为 \(0\) 否则为 \(1\)

\(0\) 的走法就是在偶数路径上走,再上到那个走法里一直走,最后再从同一个地方下来走完最后的路径

\(1\) 的同理

至于判断是否存在长度为偶数的路径 可以用冰茶姬,把一个点拆成奇点和偶点

路径长度为奇数就连奇点和偶点,为偶数就奇点连奇点

然后看看 \((u,v)\) 的两个偶点或者奇点是不是在同一个冰茶姬里就行了

Code
#include<bits/stdc++.h>
#define int long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,q;
int fa[100010],g[100010];
struct E{int x,y,z;}ee[100010];
int gcd(int x,int y){return (!y)?x:gcd(y,x%y);}
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
inline void merge(int x,int y){
	x=getfa(fa[x]),y=getfa(fa[y]);
	if(x!=y) fa[x]=y,g[y]=gcd(g[y],g[x]);
}
namespace DSU{
	int fa[200010];//i i+n 0 1
	int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
	inline void init(){for(int i=1;i<=n*2;i++) fa[i]=i;}
	inline void merge(int x,int y){x=getfa(fa[x]);y=getfa(fa[y]);if(x!=y) fa[x]=y;}
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	n=read(),m=read(),q=read();
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++){
		ee[i].x=read(),ee[i].y=read(),ee[i].z=read();
		merge(ee[i].x,ee[i].y);
		g[getfa(ee[i].x)]=gcd(ee[i].z,g[getfa(ee[i].x)]);
	}
	for(int i=1;i<=m;i++) ee[i].z/=g[getfa(ee[i].x)];
	DSU::init();
	for(int i=1,x,y,z;i<=m;i++){
		x=ee[i].x,y=ee[i].y,z=ee[i].z;
		if(z&1){
			DSU::merge(x,y+n);
			DSU::merge(x+n,y);
		}else{
			DSU::merge(x,y);
			DSU::merge(x+n,y+n);
		}
	}
	for(int i=1,x,y,k,gggg;i<=q;i++){
		x=read(),y=read(),k=read();
		if(getfa(x)!=getfa(y)){puts("NIE");continue;}
		else if(k&1){puts("0");continue;}
		else{
			gggg=gcd(k,g[getfa(x)]);
			if(DSU::getfa(x)==DSU::getfa(y)) printf("0\n");
			else printf("%lld\n",gggg);
		}
	}
	return 0;
}

C. Hack

容易发现在同一个 \(SCC\) 里的边一定不能被选择,所以先缩点

然后对于 \(SCC\) 之间的桥则可以被选择

如果没有只能经过一条的限制就可以直接跑个最小割

那么考虑有限制,拿样例举例子

image

发现最小割割掉的是上面的 \(1\) 和下面的 \(1\) ,但这样会有一条路径经过 \(2\) 条选择的边

不想让这个情况发生 于是把中间的那一条边的反向边的流量改成 \(\infty\)

这样就存在了一条从源点到汇点的路了,于是这种情况就不能成为最小割了

那么对于每一条边都把反向边的流量改成 \(\infty\) 就可以避免经过两条边了

Code
#include<bits/stdc++.h>
#define int long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,S,T,ans;
int q[110],h,t,dis[110];
int head[110],HD[110],ver[5010],edge[5010],to[5010],tot=1;
int dfn[110],low[110],id[110],col,clo,stk[110],p;
struct E{int x,y,k;}ee[2510];
vector<int> g[110];
bool vis[110];
inline void add(int x,int y,int z){
	ver[++tot]=y,edge[tot]=z,to[tot]=head[x],head[x]=tot;
	ver[++tot]=x,edge[tot]=inf,to[tot]=head[y],head[y]=tot;
}
inline bool bfs(){
	for(int i=1;i<=col;i++) dis[i]=inf;
	dis[S]=0,q[h=t=1]=S;memcpy(head,HD,sizeof(head));
	while(h<=t){
		int x=q[h++];
		for(int i=head[x];i;i=to[i]) if(edge[i]){
			int y=ver[i];
			if(dis[y]>dis[x]+1) dis[y]=dis[x]+1,q[++t]=y;
		}
		if(x==T) return true;
	}
	return false;
}
int dfs(int x,int in){
	if(x==T) return in;
	int res=in,go;
	for(int i=head[x];i;head[x]=i=to[i]) if(edge[i]){
		int y=ver[i];
		if(dis[y]==dis[x]+1){
			go=dfs(y,min(edge[i],res));
			if(go) res-=go,edge[i]-=go,edge[i^1]+=go;
			else dis[y]=0;
		}
		if(!res) break;
	}
	return in-res;
}
void tarjan(int x){
	dfn[x]=low[x]=++clo;stk[++p]=x;vis[x]=1;
	for(auto y:g[x]){
		if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
		else if(vis[y]) low[x]=min(low[x],dfn[y]);
	}
	int k;
	if(dfn[x]==low[x]){
		col++;
		do{k=stk[p--];id[k]=col;vis[k]=0;}while(k!=x);
	}
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		ee[i].x=read()+1,ee[i].y=read()+1,ee[i].k=read();
		g[ee[i].x].emplace_back(ee[i].y);
	}
	tarjan(1);if(id[1]==id[n]) puts("-1"),exit(0);
	for(int i=1,x,y;i<=m;i++){
		x=ee[i].x,y=ee[i].y;
		if(id[x]==id[y]) continue;
		add(id[x],id[y],ee[i].k);
	}
	memcpy(HD,head,sizeof(head));S=id[1],T=id[n];
	while(bfs()) ans+=dfs(S,inf);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-12-23 17:54  Max_QAQ  阅读(53)  评论(0编辑  收藏  举报