CSP模拟27

考的有一点意外,出乎意料。

[CF1060E] Sergey and Subway

题目链接

考场上打假了,乐。

\(dis_{i,j}\) 表示 \(i\)\(j\) 的树上距离。

很容易发现,答案其实就是:

\[\sum_{i=1}^n \sum_{j=i+1}^n \lceil \frac{dis_{i,j}}{2} \rceil \]

其实就是所有点对的距离,加上距离为奇数的点对的个数,最后除以二就可以了。

复杂度 \(O(n)\)

点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>

#define int long long

const int MAXN=2e5+10;

using namespace std;

inline int read() {
	int f=1,x=0;
	char ch=getchar();
	while(ch>'9' || ch<'0') {
		if(ch=='-') {
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return f*x;
}

int n,cnt,ans,tot,s0,s1;
int dep[MAXN],siz[MAXN],fa[MAXN];
struct edge {
	int to,next;
}a[MAXN<<1];
int head[MAXN];

void add(int u,int v) {
	a[++cnt].to=v;
	a[cnt].next=head[u];
	head[u]=cnt;
}

int g(int x) {
	if(x&1) return (x+1)/2;
	return x/2;
} 

void dfs(int now,int father) {
	dep[now]=dep[father]+1;
	siz[now]=1;
	if(dep[now]&1) s1++;
	else s0++;
	for(int i=head[now];i;i=a[i].next) {
		int v=a[i].to;
		if(v==father) continue;
		dfs(v,now);
		siz[now]+=siz[v];
	}
}

void dfs1(int now,int father) {
	for(int i=head[now];i;i=a[i].next) {
		int v=a[i].to;
		if(v==father) continue;
		tot+=(siz[v]*(n-siz[v]));
		dfs1(v,now);
	}
} 

signed main() {
	
	n=read();
	for(int i=1;i<n;i++) {
		int u=read(),v=read();
		add(u,v);
		add(v,u);
	}
	
	dfs(1,0);
	
	int sum=0;
	for(int i=1;i<=n;i++) {
		if(dep[i]&1) {
			sum+=s0;
		}
		else {
			sum+=s1;
		}
	}
	sum/=2;
	
	dfs1(1,0);
	
	printf("%lld",(tot+sum)/2);
	
	return 0;
} 
/*
4
1 2
1 3
1 4
*/ 

[CF979D] Kuro and GCD and XOR and SUM

题目链接

可以先考虑暴力,拿下10分。

观察数据范围,发现值域比较的小。

先考虑第二个条件,$ k|\gcd(v,x) $ ,在这里其实就是 \(k|v\)\(k|x\) 的意思,

考虑第三个条件,异或最大值经典做法是 01-tire ,这里只不过加了几个限制。

对于第二个限制,我们可以在加入元素时,根据元素的约数(可以预处理出来)各建一个 Tire ,在每个约数的 Tire 内插入这个元素,查询 \(k\) 时直接在 \(k\) 的那个 Tire 里找就可以。

对于第一个限制,我们考虑对于 Trie 的每个节点设一个 \(mi\) 值,表示的是经过这个节点的数的最小值,如果 \(mi<k\) ,就不可以往这里走,换另一边。

复杂度 ,时间复杂度为 \(O(x\log{x})\) ,空间复杂度为 \(O(x\log{x})\)

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>

#define int long long

const int MAXN=1e7;
const int M=1e5+10;
const int inf=1e18;

using namespace std;

inline int read() {
	int f=1,x=0;
	char ch=getchar();
	while(ch>'9' || ch<'0') {
		if(ch=='-') {
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return f*x;
}

int q,cnt;
struct Tire {
	int son[2];
}tr[MAXN];
int mi[MAXN],rt[M];
bool vis[M],a[M];

vector <int> y[MAXN];

void init() {
	for(int i=1;i<=1e5;i++) {
		for(int j=i;j<=1e5;j+=i) {
			y[j].push_back(i);
		}
	} 
} 

void insert(int root,int x) {
	int now=root;
	mi[root]=min(mi[root],x);
	for(int i=31;i>=0;i--) {
		int ch=((x>>i)&1);
		if(!tr[now].son[ch]) {
			tr[now].son[ch]=++cnt;
		}
		now=tr[now].son[ch];
		mi[now]=min(mi[now],x);
	}
} 

int query(int k,int x,int s) {
	int now=rt[k];
	if(x%k!=0 || mi[now]+x>s) {
		return -1;
	}
	int ans=0;
	for(int i=31;i>=0;i--) {
		int ch=((x>>i)&1);
		if(tr[now].son[!ch] && mi[tr[now].son[!ch]]+x<=s) {
			ans+=((ch^1)<<i);
			now=tr[now].son[!ch];
		} 
		else {
			ans+=(ch<<i);
			now=tr[now].son[ch];
		}
	}
	return ans;
}

signed main() {
	
	init();
	
	for(int i=0;i<=1e7;i++) {
		mi[i]=inf;
	}
	
	q=read();
	for(int g=1;g<=q;g++) {
		int op=read();
		if(op==1) {
			int x=read();
			if(a[x]) continue;
			a[x]=1;
			
			for(int i=0;i<y[x].size();i++) {
				int now=y[x][i];
				if(!vis[now]) {
					rt[now]=++cnt;
					vis[now]=1;
				}
				insert(rt[now],x);
			} 
		}
		if(op==2) {
			int x=read(),k=read(),s=read();
			printf("%lld\n",query(k,x,s));
		}
	}
	return 0;
} 
/*
5
1 1
1 2
2 1 1 3
2 1 1 2
2 1 1 1
*/
/*
2
1
-1
*/ 

[CF1101F] Trucks and Cities

题目链接

直接暴力二分,再加上一些神奇的优化,就可以跑的飞快,非常的nice,在OJ上拿下最优解 (但不是正解)

复杂度 \(O(能过)\)

点击查看代码

#include <iostream>
#include <cstdio>

#define int long long

const int MAXN=410;
const int inf=2e18;

using namespace std;

inline int read() {
	int f=1,x=0;
	char ch=getchar();
	while(ch>'9' || ch<'0') {
		if(ch=='-') {
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return f*x;
}

int n,m,s,t,c,r,ans;
int a[MAXN];

bool check(int x) {
	int rr=r,now=x;
	bool flag=1;
	for(int i=s;i<t;i++) {
		int sp=(a[i+1]-a[i])*c;
		if(sp>now) {
			if(rr==0) return 0;
			now=x;
			rr--;
			if(now<sp) {
				return 0;
			}
		}
		now-=sp;
	}
	return 1;
}

signed main() {
	
	//freopen("drive.in","r",stdin);
//	freopen("drive.out","w",stdout);
	n=read() ,m=read();
	for(int i=1;i<=n;i++) {
		a[i]=read();
	}
	for(int i=1;i<=m;i++) {
		s=read() ,t=read() ,c=read() ,r=read();
		int z=ans,y=inf,sum=0;
		if(check(ans)) continue;
		while(z<=y) {
			int mid=(z+y)>>1;
			if(check(mid)) {
				sum=mid;
				y=mid-1;
			}
			else {
				z=mid+1;
			}
		}
		ans=max(sum,ans);
	}

	printf("%lld\n",ans);
	
	return 0;
} 
/*
7 6
2 5 7 10 14 15 17
1 3 10 0
1 7 12 7
4 5 13 3
4 7 10 1
4 7 10 1
1 5 11 2
*/

[JOISC2022] Jail

题目链接

思路比较的简单,对于先走和后走,我们可以得出一个结论:

如果存在方案,一定有一种方案使每个人都是从起点不停歇地走到终点。

简单证明一下,如果 \(A\) 为了让 \(B\) 能够顺利通过而让路,从而使 \(B\) 先到终点,那么 \(A\) 可以先走到终点,然后 \(B\) 再走,多个点也是一样的。

那么我们很容易得出两条结论:

1.如果 A 的起点在 B 的路径上,那么 A 一定比 B 先走。
2.如果 A 的终点在 B 的路径上,那么 B 一定比 A 先走。

根据这个关系,可以建出一张有向图,暴力跳路径,然后加边,再跑一遍拓扑来判断是否有环。

但是这样暴跳复杂度为 \(O(T(m(n+\log n)))\) ,不可过.

考虑优化建边,很容易想到 树链剖分 和 线段树优化建边。

我们设 \(u_i\)\(v_i\) 分别表示 第 \(i\) 条路径的起点与终点。

先处理起点,对于起点,我们想把路径上的所有除自己之外的起点和路径连边。
首先我们把 \(i\)\(u_i\) 连边,再把路径 在线段树上对应的区间 在 内向树上 和 \(i\) 连边,这样连边我们会让路径上所有的起点向 该路径的起点连边 ,但是这样连边天然成环,因为这条路径的起点经过线段树和路径的编号连向了自己。那么我们在连边时,把路径起点排除掉就可以了。

对于终点思路完全相同,只不过所有边的方向反过来(包括线段树内边)。

复杂度 \(O(Tm \log n)\)

点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>

#define int long long

const int MAXN=5e6+10;

using namespace std;

inline int read() {
	int f=1,x=0;
	char ch=getchar();
	while(ch>'9' || ch<'0') {
		if(ch=='-') {
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	return f*x;
}

int T,n,m,cnt,cnt1,tim;
struct edge {
	int next,to;
}a[MAXN<<1],e[MAXN<<1];
int head[MAXN],head1[MAXN],vis[MAXN];

int dep[MAXN],siz[MAXN],son[MAXN],fa[MAXN];
int top[MAXN],id[MAXN],mp[MAXN],in[MAXN];

void init() {
	for(int i=1;i<=n;i++) {
		dep[i]=siz[i]=fa[i]=son[i]=0;
		id[i]=top[i]=head[i]=0;
	}
	for(int i=1;i<=cnt;i++) {
		a[i].to=a[i].next=0;
	}
	for(int i=1;i<=cnt1;i++) {
		e[i].to=e[i].next=0;
	}
	
	for(int i=1;i<=n*8+m;i++) {
		head1[i]=0;
		in[i]=mp[i]=0;
	}
	cnt=0,n=0,m=0,cnt1=0,tim=0;
}


void add(int u,int v) {
	a[++cnt].to=v;
	a[cnt].next=head[u];
	head[u]=cnt;
}
 
void add1(int u,int v) {
	e[++cnt1].to=v;
	e[cnt1].next=head1[u];
	head1[u]=cnt1;
	in[v]++;
}

void dfs1(int now,int father) {
	dep[now]=dep[father]+1;
	fa[now]=father;
	siz[now]=1;
	int maxson=-1;
	for(int i=head[now];i;i=a[i].next) {
		int v=a[i].to;
		if(v==father) continue;
		dfs1(v,now);
		siz[now]+=siz[v];
		if(siz[v]>maxson) {
			maxson=siz[v];
			son[now]=v;
		}
	}
}

void dfs2(int now,int topf) {
	top[now]=topf;
	id[now]=++tim;
	if(son[now]) dfs2(son[now],topf);
	for(int i=head[now];i;i=a[i].next) {
		int v=a[i].to;
		if(v==fa[now] || v==son[now]) continue;
		dfs2(v,v);
	}
}

void build(int k,int l,int r) {
	if(l==r) {
		mp[l]=k;
		return;
	}	
	add1(k,k<<1) ,add1(k,k<<1|1);
	add1((k<<1)+4*n,k+4*n) ,add1((k<<1|1)+4*n,k+4*n);
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}

void up_date1(int k,int l,int r,int L,int R,int pos) {//外向树 
	if(L<=l && r<=R) {
		add1(pos,k);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid) up_date1(k<<1,l,mid,L,R,pos);
	if(R>mid) up_date1(k<<1|1,mid+1,r,L,R,pos);
}

void up_date2(int k,int l,int r,int L,int R,int pos) {//内向树 
	if(L<=l && r<=R) {
		add1(k+4*n,pos);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid) up_date2(k<<1,l,mid,L,R,pos);
	if(R>mid) up_date2(k<<1|1,mid+1,r,L,R,pos);
}

void change(int u,int v,int pos) {
	add1(mp[id[u]],8*n+pos);
	add1(8*n+pos,mp[id[v]]+4*n);
	int x=u,y=v;
	while(top[x]!=top[y]) {
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		if(x!=u) up_date1(1,1,n,id[top[x]],id[x],8*n+pos);
		else if(top[x]!=x) up_date1(1,1,n,id[top[x]],id[x]-1,8*n+pos);
		if(x!=v) up_date2(1,1,n,id[top[x]],id[x],8*n+pos);
		else if(top[x]!=x) up_date2(1,1,n,id[top[x]],id[x]-1,8*n+pos);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	if(x!=u && y!=u) up_date1(1,1,n,id[x],id[y],8*n+pos);
	else if(x!=y) {
		if(x==u) up_date1(1,1,n,id[x]+1,id[y],8*n+pos);
		else up_date1(1,1,n,id[x],id[y]-1,8*n+pos);
	}
	
	if(x!=v && y!=v) up_date2(1,1,n,id[x],id[y],8*n+pos);
	else if(x!=y) {
		if(x==v) up_date2(1,1,n,id[x]+1,id[y],8*n+pos);
		else up_date2(1,1,n,id[x],id[y]-1,8*n+pos);
	}
}

queue <int> q;

signed main() {
	
	freopen("prison.in","r",stdin);
	freopen("prison.out","w",stdout);
	
	T=read();
	while(T--) {
		init();
		n=read();
		for(int i=1;i<n;i++) {
			int u=read(),v=read();
			add(u,v);
			add(v,u);
		}
		
		dfs1(1,0);
		dfs2(1,1);
		
		m=read();
		
		build(1,1,n);
		
		for(int i=1;i<=m;i++) {
			int u=read(),v=read();
			change(u,v,i);
		}
		
		for(int i=1;i<=8*n+m;i++) {
			if(!in[i]) {
				q.push(i);
			}
		}
		
		while(!q.empty()) {
			int now=q.front();
			q.pop();
			for(int i=head1[now];i;i=e[i].next) {
				int v=e[i].to;
				in[v]--;
				if(in[v]==0) {
					q.push(v);
				}
			}
		}
		
		bool flag=1;
		for(int i=1;i<=8*n+m;i++) {
			if(in[i]) {
				flag=0;
				break;
			}
		}
		if(flag==1) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
} 

posted @ 2023-08-21 18:46  Trmp  阅读(10)  评论(0编辑  收藏  举报
-->