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