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; }