2019-11-1 CSP-S模拟

题目

内网题目地址

T1

手动玩几组样例就可以发现:如果给出的马匹数的GCD不能整除a,则不满足第一个条件;如果马匹数最小的那个的平方根 ≤ \le a,则不满足第二个条件;
注意:如果在判断第二个条件时计算 a 2 a^2 a2而不是 c i \sqrt{c_i} ci ,会 l o n g   l o n g long \ long long long

#include <bits/stdc++.h>
using namespace std;
const int N=700050;
int n,q,b[N];
long long minn=1e18;
long long c[N],a[N],GCD,h;
inline long long read(){
	long long cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		c[i]=read();minn=min(c[i],minn);GCD=gcd(GCD,c[i]);
	}
	q=read();
	int h=ceil(sqrt(minn));
	for(int i=1;i<=q;i++){
		a[i]=read(),b[i]=read();
		if(b[i]==1){
			if(GCD%a[i]==0){
				printf("Yes\n");
			}
			else{
				printf("No\n");
			}
		}
		if(b[i]==2){
			if(h<=a[i]){
				printf("No\n");
			}
			else{
				printf("Yes\n");
			}
		}
	}
	return 0;
}

T2

思路: 桶套桶
第一层桶:记录标本串中每个字母出现的次数
c n t cnt cnt标本串的第一层桶 c n t 2 cnt2 cnt2枚举区间的第一层桶
第二层桶:以第一层桶为下标,统计每个元素出现次数的次数
g g g标本串的第二层桶 f f f枚举区间的第二层桶
记录一个 t o t tot tot,方便我们计算当前是否合法
当枚举区间的子串和标本串的第二层桶各项的差的绝对值之和为 0 0 0时,这两者中第二层桶的值一一对应,即可以通过变换得到
感谢kma的blog

#include <bits/stdc++.h>
#define N 2010
#define D 26
using namespace std;
bool isabc(char ch){return (ch>='a')&&(ch<='z');}
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
int T,len,m,Q;
int L,R,tot;
int cnt[D],cnt2[D];
int q[N][D],g[N],f[N];
bool vis[N];
int tmp;
char ch;
int s[N],ans;
void add(int x,int sig){
	if(f[cnt2[x]]>g[cnt2[x]])tot--;else tot++;
	f[cnt2[x]]--;
	cnt2[x]+=sig;
	if(f[cnt2[x]]>=g[cnt2[x]])tot++;else tot--;
	f[cnt2[x]]++;//
	return;
}
int main(){
	T=read();
	while(T--){
		m=read(),Q=read();
		tmp=0;ch=getchar();
		for(;!isabc(ch);ch=getchar());
		for(;isabc(ch);ch=getchar())	s[++tmp]=ch-'a';
		for(int i=1;i<=m;i++){
			for(int j=0;j<D;j++){q[i][j]=q[i-1][j];}
			q[i][s[i]]++;
		}
		while(Q--){
			for(int i=0;i<D;i++){cnt[i]=cnt2[i]=0;}
			L=read(),R=read();
			len=R-L+1;
			for(int i=0;i<D;i++){
				cnt[i]=q[R][i]-q[L-1][i];
				cnt2[i]=q[len][i];//?? 
				g[cnt[i]]++;f[cnt2[i]]++;
			}
			tot=0;
			for(int i=0;i<D;i++){
				if(!vis[cnt[i]]){
					vis[cnt[i]]=true;
					tot+=g[cnt[i]];
				}
			}
			for(int i=0;i<D;i++){
				vis[cnt[i]]=false;
			}
			for(int i=0;i<D;i++){
				if(!vis[cnt2[i]]){
					vis[cnt2[i]]=true;
					if(f[cnt2[i]]>g[cnt2[i]])tot+=f[cnt2[i]]-2*g[cnt2[i]];//?? 
					else tot-=f[cnt2[i]];
				}
			}
			for(int i=0;i<D;i++){vis[cnt2[i]]=false;}
			ans=0;
			if(!tot)ans++;
			for(int i=len+1;i<=m;i++){
				add(s[i],1);add(s[i-len],-1);
				if(!tot)	ans++;
			}
			printf("%d\n",ans);
			for(int i=0;i<D;i++){
				g[cnt[i]]--;f[cnt2[i]]--;
			}
		}
	}
	return 0;
}

T3

由题意

  1. 一个节点会给他到首都的最短路径上的所有城市再贡献 a u a_u au点魅力值
  2. 因为不想让自己一开始就受不了或者心理落差太大,作为起点的城市的⁦不能是这条路径经过的所有城市的⁦的最大值或者最小值.

故可以得到:当 d e p [ u ] > d e p [ v ] & & u , v dep[u]>dep[v]\&\&u,v dep[u]>dep[v]&&u,v在一条链上时, f u > f v f_u> f_v fu>fv
即:一个节点子树上的点的 f f f值必定小于它自己的 f f f值。
得到思路:对于一个点 i i i g i g_i gi是所有 f j < f i f_j<f_i fj<fi j j j不在以 i i i的子树中的节点 j j j ∑ f j \sum{f_j} fj
f r o m   s o l u t i o n : from\ solution: from solution:

我们考虑如何来计算gi
由于按位或运算每一个二进制位是互不干扰的,所以我们可以对于每一个二进制位分别考虑答案是多少,再累加起来。
所以,我们现在来一位位考虑。
一个关于按位或的基础性质:如果这些数当中有一个在这一位上是1,那按位或起来的结果在这一位上就是1;否则是0。

首先,因为gi是所有fj值比fi小的,且不在以i为根的子树中的fj的和。我们立马就会想到可以按照fi排序,那样我们按照fi由小到大处理,统计到fi的时候就可以统计出所有比fi小的f,在当前处理的这一二进制位上1的个数cnt。

由于在i的子树中,所有节点的f值都小于fi,所以我们这个cnt要减去以i为根的子树中(不包括i自己)在当前二进制位下1的个数hi
如果cnt大于0,我们就可以知道gi在当前这一二进制位下是1。
至于hi,使用类似于fi的方法统计即可。

时间复杂度: O ( n l o g 2 n ) O(nlog2n) O(nlog2n)
内网solution

代码(非二进制拆位):
先用 d f s dfs dfs预处理出 d f s dfs dfs序, f f f值,获取最大的 f f f值(树状数组建立的区间)
c a l c calc calc:预处理出它 d f s dfs dfs序的前缀和后缀
p o s pos pos d f s dfs dfs序对应的原点
c c c:前缀或的和(前缀处理 f f f或的和)
p r e pre pre:前缀, d f s dfs dfs序在它前面的
s u f suf suf:后缀, d f s dfs dfs序在它后面的
树状数组理解:
树状数组下标:建立在或的和上,把或的和二进制分成一段一段的区间
c [ x ] c[x] c[x]:以 x x x为根的子树中所有叶节点的或之和
预处理出 d f s dfs dfs序的原因:对于一棵子树,其中 d f s dfs dfs序一定是一个连续的 ( l , r ) (l,r) (l,r)的区间,符合题意的所有点就是 d f s dfs dfs序在 [ 1 , l − 1 ] ∪ [ r + 1 , n ] [1,l-1]∪[r+1,n] [1,l1][r+1,n]的点。

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 1e6 + 5, M = 1e7 + 5;
int lowbit(int x){
	return x&(-x);
}
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int fir[N], nxt[N << 1], to[N << 1], tot;
void add(int x, int y){
	nxt[++tot] = fir[x], fir[x] = tot, to[tot] = y;
}
int n, Cap, Mx;
int a[N], ans[N];
int in[N], out[N], pos[N], ind;
struct BIT{
	int c[M];
	void clear(){memset(c,0,sizeof(c));}
	void add(int x,int v){for(;x<=Mx;x+=lowbit(x))c[x]|=v;}
	int ask(int x){int ans=0;for(;x;x-=lowbit(x))	ans|=c[x];	return ans;}
}bit;
void dfs_(int u,int fa){
	in[u]=++ind;pos[ind]=u;
	for(int i=fir[u];i;i=nxt[i]){
		int v=to[i];
		if(v==fa)	continue;
		dfs_(v,u);a[u]+=a[v];
	}
	out[u]=ind;Mx=max(Mx,a[u]);
}
vector<int> pre[N],suf[N];
int calc(int x){
	if(in[x]>1)	pre[in[x]-1].push_back(x);
	if(out[x]<n)	suf[out[x]+1].push_back(x);
}
int main(){
	int size=40<<20;//40M
    __asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));//提交用这个 
	n=read(),Cap=read();
	for(int i=1;i<n;i++){
		int x,y;x=read(),y=read();add(x,y);add(y,x);
	}
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	dfs_(Cap,0);
	for(int i=1;i<=n;i++)	calc(i);
	for(int i=1;i<=n;i++){
		bit.add(a[pos[i]],a[pos[i]]);
		for(int j=0;j<pre[i].size();j++){
			int nx=pre[i][j];
			ans[nx]|=bit.ask(a[nx]-1);
		}
	}
	bit.clear();
	for(int i=n;i>=1;i--){
		bit.add(a[pos[i]],a[pos[i]]);
		for(int j=0;j<suf[i].size();j++){
			int nx=suf[i][j];
			ans[nx]|=bit.ask(a[nx]-1);
		}
	}
	for(int i=1;i<=n;i++){
		printf("%d ",ans[i]);
	}
	exit(0);
}

标程:

#include<bits/stdc++.h>
#define LL long long
#define MAXN 500000
using namespace std;
template<typename T>void Read(T &cn)
{
	char c;int sig = 1;
	while(!isdigit(c = getchar()))if(c == '-')sig = -1; cn = c-48;
	while(isdigit(c = getchar()))cn = cn*10+c-48; cn*=sig;
}
template<typename T>void Write(T cn)
{
	if(cn < 0) {putchar('-'); cn = 0-cn; }
	int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
	while(cn)cm = cm*10+cn%10,cn/=10,wei++;
	while(wei--)putchar(cm%10+48),cm/=10;
	putchar(cx+48);
}
struct qwe{
	int a,b,ne;
};
struct qwer{
	int a,b;
	inline friend bool operator <(qwer a,qwer b) {return a.a < b.a; }
	void mk(int cn,int cm) {a = cn; b = cm; }
};
qwe a[MAXN*2+1];
int alen;
int head[MAXN+1];
int n,ro;
int fa[MAXN+1],f[MAXN+1],zhi[MAXN+1];
qwer lie[MAXN+1];
int ans[MAXN+1];
int ge[MAXN+1][24];
void lian(int cn,int cm)
{
	a[++alen].a = cn;
	a[alen].b = cm;
	a[alen].ne = head[cn];
	head[cn] = alen;
}
void dfs(int cn)
{
	f[cn] = zhi[cn];
	for(int i = head[cn];i;i = a[i].ne)
	{
		int y = a[i].b;
		if(y == fa[cn])continue;
		fa[y] = cn;
		dfs(y);
		f[cn] = f[cn] + f[y];
	}
}
void set_ge(int cn,int cm)
{
	for(int i = 0;i<=cm;i++)ge[cn][i] = 0;
	for(int i = head[cn];i;i = a[i].ne)
	{
		int y = a[i].b;
		if(y == fa[cn])continue;
		set_ge(y,cm);
		for(int j = 0;j<=cm;j++)ge[cn][j] = ge[cn][j] + ge[y][j] + ((f[y]&(1<<j)) != 0);
	}
}
int erwei(int cn) {int guo = 0; while(cn)guo++,cn >>= 1; return guo; }
void zuo(int cn)
{
	int he = 0,lei = 0; lie[0].a = lie[1].a-1;
	for(int i = 1;i<=n;i++)
	{
		if(lie[i-1].a != lie[i].a)he += lei,lei = 0;
		ans[lie[i].b] |= (1<<cn)*(he > ge[lie[i].b][cn]);
		lei += ((f[lie[i].b] & (1<<cn)) != 0);
	}
}
int main()
{
	freopen("forever.in","r",stdin);
	freopen("forever.out","w",stdout);
	Read(n); Read(ro);
	alen = 0; memset(head,0,sizeof(head));
	for(int i = 1;i<n;i++)
	{
		int bx,by; Read(bx); Read(by);
		lian(bx,by); lian(by,bx);
	}
	for(int i = 1;i<=n;i++)Read(zhi[i]);
	fa[ro] = 0;
	dfs(ro);
	for(int i = 1;i<=n;i++)lie[i].mk(f[i],i);
	sort(lie+1,lie+n+1);
	for(int i = 1;i<=n;i++)ans[i] = 0; int lin = erwei(f[ro]);
	set_ge(ro,lin);
	for(int i = 0;i<=lin;i++)zuo(i);
	for(int i = 1;i<=n;i++)Write(ans[i]),putchar(' '); putchar('\n');
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值