T1

这道题,挺明显的,就是有一些细节。

对于要求1:要求\(\gcd\) % \(a==0\) \(\gcd\)是所有数共同的\(\gcd\)

对于要求2:要求\(a*a<min(c)\),\(c\)是每一组的马匹数

然后就可以\(o(n)\)做了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define LL long long 
using namespace std;
LL read(){
	LL 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*10+ch-'0';ch=getchar();}
	return x*f; 
}
template <typename T>void Write(T cn){
	if(cn<0){putchar('-');cn=0-cn;}
	LL wei=0;T cm=0;LL 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);
}
LL n,gcd,minn,limit,q;
LL get_gcd(LL x,LL y){
	if(y==0) return x;
	return get_gcd(y,x%y);
}
int main(){
	freopen("queue.in","r",stdin);
	freopen("queue.out","w",stdout);
	n=read();gcd=read();minn=gcd;
	for(LL i=2,x;i<=n;i++){
		x=read();minn=min(minn,x);
		if(x<gcd) swap(x,gcd); 
		gcd=get_gcd(x,gcd);
	}
	limit=sqrt(minn);
	if(limit*limit==minn) limit--;
	q=read();
	for(LL i=1,a,b;i<=q;i++){
		a=read();b=read();
		if(b==1){
			if(gcd%a==0) printf("Yes\n");
			else printf("No\n");
		}
		else{
			if(a<=limit) printf("Yes\n");
			else printf("No\n");
		}
	}
	return 0;
}

T2

这道题我在考场上并不是正解,但是居然卡着时间(\(1.964s\))过了。

先说一下考场上我的解法吧。

首先将询问离线,将询问按照\(l-r+1\)从小到大排序。 对于相同长度的询问显然不用再次处理,且相邻长度不同的询问,只需要修改一些(复杂度\(O(n)\)),十分优秀。 而我在每个询问的处理实在是太烂了,正解是对于每个询问的处理十分优秀。。

先看我的代码(类似于莫队的思想)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define LL long long 
using namespace std;
const int maxn=2010;
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*10+ch-'0';ch=getchar();}
	return x*f; 
}
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 node{
	int tong[2010],eng[30],maxx,tag;
	void clear(){
		memset(tong,0,sizeof(tong));
		memset(eng,0,sizeof(eng));
		maxx=0;tag=0;
	}
}yuan[2010];
struct wow{
	int l,r,len,id;
}ques[2010];
int t,n,q,ans[maxn];
char shawn[2010];
inline bool cmpl(wow x,wow y){
	return x.len<y.len;
}
int main(){
	freopen("element.in","r",stdin);
	freopen("element.out","w",stdout);
	t=read();
	while(t--){
		n=read();q=read();
		scanf("%s",shawn+1);
		memset(ans,0,sizeof(ans));
		for(int i=1;i<=n;i++) yuan[i].clear();
		for(int i=1;i<=n;i++){
			yuan[i].tong[1]=1;
			yuan[i].eng[shawn[i]-'a']=1;
			yuan[i].maxx=1;yuan[i].tag=0;
		}
		for(int i=1;i<=q;i++)
			ques[i].l=read(),ques[i].r=read(),ques[i].len=(ques[i].r-ques[i].l+1),ques[i].id=i;	
		sort(ques+1,ques+1+q,cmpl);
		int qnow=1,nowlen=1,tag=0;
		while(1){
			if(nowlen<ques[qnow].len){
				for(int i=1;i<=n-nowlen;i++){
					if(yuan[i].eng[shawn[i+nowlen]-'a']){
						yuan[i].tong[yuan[i].eng[shawn[i+nowlen]-'a']]--;
						yuan[i].eng[shawn[i+nowlen]-'a']++;
						yuan[i].tong[yuan[i].eng[shawn[i+nowlen]-'a']]++;
						yuan[i].tag=0;
					}
					else{
						yuan[i].maxx++;
						yuan[i].eng[shawn[i+nowlen]-'a']++;
						yuan[i].tong[1]++;
						yuan[i].tag=0;
					}
				}
				nowlen++;
				continue;	
			}
			int ql=ques[qnow].l;
			if(yuan[ql].tag){
				for(int i=1;i<=n-nowlen+1;i++){
					if(yuan[ql].tag==yuan[i].tag){
						ans[ques[qnow].id]++;
					}
				}
			}
			else{
				yuan[ql].tag=++tag;
				for(int i=1;i<=n-nowlen+1;i++){
					if(yuan[ql].maxx!=yuan[i].maxx) continue;
					int elenow=0,flag=0;
					for(int j=1;j<=2000;j++){
						if(yuan[ql].tong[j]!=yuan[i].tong[j]) break;
						if(elenow==yuan[ql].maxx){
							flag=1;break;
						}	
						elenow+=yuan[ql].tong[j];
					}
					if(flag){
						ans[ques[qnow].id]++;
						yuan[i].tag=yuan[ql].tag;	
					}	
				}
			}
			if(qnow==q) break;
			qnow++;
		}
		for(int i=1;i<=q;i++) Write(ans[i]),printf("\n");
	}
}

再来讲正解:正解并没有将询问离线。首先我们考虑两个区间相似的条件:出现次数相同的字母种类相同。这启发我们用桶来装。然而对于一个字母,其出现次数可能高达\(2000\);对于一个桶,其有值的下标最多只有26个,如果对于每一次我们都扫一遍桶,是不是太不划算了呢?(居然打到了\(o(n)\)的常数震撼我妈) 因此我们用一个队列来存储标本桶有值的下标,然后用标本桶去与当前区间的桶进行比较,如果完全相同就说明这两个区间相似,\(ans++\)

(这是PMH的代码,十分巧妙,然而标算的常数更小,但是更难想到)

#include<bits/stdc++.h>
using namespace std;
int dep[27],tong2[3000];
int tong1[3000];
int re(){
	int i=0;
	char c=getchar();
	while(!isdigit(c)) c=getchar();
	for(;isdigit(c);c=getchar()) i=(i<<1)+(i<<3)+c-'0';
	return i;
}
char a[2005];
int num;
queue<int>q;
bool cmp(){
	for(int i=1;i<=num;i++){
		int x=q.front();
		q.pop();
		q.push(x);
		if(tong2[x]!=tong1[x]) return false;
	}
	return true;
}
int main(){
	freopen("element.in","r",stdin);
	freopen("element.out","w",stdout);
	int t=re();
	while(t--){
		int n=re(),qww=re();
		scanf("%s",a+1);
		for(int i=1;i<=qww;i++){
			while(!q.empty()){
				int x=q.front();
				q.pop();
				tong1[x]=0;
			}
			int l=re(),r=re();
			for(int j=l;j<=r;j++){
				dep[a[j]-'a']++;
			}
			num=0;
			for(int j=0;j<26;j++){
				if(tong1[dep[j]]==0){
					num++;q.push(dep[j]);
				}
				tong1[dep[j]]++;
			}
			r=r-l+1;memset(dep,0,sizeof(dep));
			tong2[0]=26;
			for(int j=1;j<=r;j++){
				tong2[dep[a[j]-'a']]--;
				dep[a[j]-'a']++;
				tong2[dep[a[j]-'a']]++;
			}
			
			int ans=0;
			if(cmp()) ans++;
			for(int j=r+1;j<=n;j++){
				tong2[dep[a[j-r]-'a']]--;
				dep[a[j-r]-'a']--;
				tong2[dep[a[j-r]-'a']]++;
				tong2[dep[a[j]-'a']]--;
				dep[a[j]-'a']++;
				tong2[dep[a[j]-'a']]++;	
				if(cmp()) ans++;			
			}
			printf("%d\n",ans);
			for(int j=0;j<26;j++){
				if(dep[j]){
					tong2[dep[j]]=0;
					dep[j]=0;
				}
			}
			tong2[0]=0;
		}
	}
	return 0;
}

这是标程,他是用绝对值的思想来处理的两个桶的比较

#include<bits/stdc++.h>
#define N 2010
#define D 26
using namespace std;
bool isabc(char ch){
	return (ch>='a')&&(ch<='z');
}
template<typename T> void Read(T &X){
	X=0;char C=getchar();
	for (;!isdigit(C);C=getchar());
	for (; isdigit(C);C=getchar()) X=(X<<3)+(X<<1)+C-'0';
}
int T,len,m,Q;
int L,R,tot;
int cnt[D],cnt2[D];
int q[N][D];
int g[N],f[N];
bool vis[N];
int tmp;
char ch;
int s[N];
int 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(){
	freopen("element.in","r",stdin);
	freopen("element.out","w",stdout);
	Read(T);
	while (T--){
		Read(m);Read(Q);
		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;
			Read(L);Read(R);
			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]]--;
			}
		}
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T3

这道题暴力可以得\(40pts\)的,然而我竟然爆零了(剪枝剪错QAQ

正解的思想,十分毒瘤。因为我的脑回路不毒瘤,所以我并没有想不出来,甚至看了题解也看不懂QAQ

那么讲一讲另一种满分算法:

首先分析一下这道题的性质:因为所有的儿子都对父亲有自己权值的贡献,所以子树节点一定大于任意子树内权值,相当于子树节点的终点一定不在子树中。而起点向上走一个就已经满足最大值不是自己的条件了。因此显然起点的终点满足\(f_{end}<f{i}\)并且终点不在子树中。

而在\(dfs\)序中,子树的\(dfs\)序一定在子树节点\(in\)\(out\)之间。因此终点所需要满足的条件可以转换为\((f_{end}<fi)\) && \((in_{end}<in_{i}\) || \(out_{end}>out_{i})\) 这是不是有点像二维偏序问题?

我们考虑分别对\(in,out\) 排序,以\(f\)为树状数组的下标,存储前缀或(类比于前缀和)

然后就完了QAQ

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define int long long
using namespace std;
const int maxn=5000000;
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*10+ch-'0';ch=getchar();}
	return x*f;
}
int times,n,f[maxn],tree[maxn<<1],root,ans[maxn],maxx;
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
struct node{
	int in,id;
	#define in1(x) ques1[x].in
	#define id1(x) ques1[x].id
	#define in2(x) ques2[x].in
	#define id2(x) ques2[x].id
}ques1[maxn],ques2[maxn];
void add(int x,int y){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;}
void dfs(int x,int fa){
	in1(x)=++times;
	for(int i=fir[x];i;i=nxt[i]){
		int y=to[i];if(y==fa) continue ;
		dfs(y,x);
		f[x]+=f[y];
	}
	
	in2(x)=++times;
}
inline bool cmpl(node x,node y){
	return x.in<y.in;
}
int query(int x){
	int ans=0;
	for(;x;x-=(x&-x)){
		ans|=tree[x];
	}
	return ans;
}
void update(int x,int y){
	for(;x<=maxx;x+=(x&-x))
		tree[x]|=y;
}
int query1(int x){
	int ans=0;
	for(;x;x-=(x&-x)){
		ans|=tree[x];
	}
	return ans;
}
void update1(int x,int y){
	for(;x<=maxx;x+=(x&-x))
		tree[x]|=y;
}
inline bool cmpll(node x,node y){
	return x.in>y.in;
}
signed main(){
//	freopen("forever.in","r",stdin);
//	freopen("forever.out","w",stdout);
	n=read();root=read();
	for(int i=1,x,y;i<=n-1;i++){
		x=read();y=read();
		add(x,y);add(y,x);
	}
	for(int i=1;i<=n;i++)
		f[i]=read(),id1(i)=id2(i)=i;
	dfs(root,0);
	for(int i=1;i<=n;i++)
		maxx|=f[i];
	sort(ques1+1,ques1+1+n,cmpl);
	sort(ques2+1,ques2+1+n,cmpll);
	for(int i=1;i<=n;i++){
		ans[ques1[i].id]|=query(f[ques1[i].id]-1);
		update(f[ques1[i].id],f[ques1[i].id]);
	}
	memset(tree,0,sizeof(tree));
	for(int i=1;i<=n;i++){
		ans[ques2[i].id]|=query1(f[ques2[i].id]-1);
		update1(f[ques2[i].id],f[ques2[i].id]);
	}
	for(int i=1;i<=n;i++)
		printf("%d ",ans[i]);
	return 0;
}
posted on 2019-11-01 20:42  萌德真帅  阅读(292)  评论(0编辑  收藏  举报