2019-11-1

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

猜你喜欢

转载自www.cnblogs.com/mendessy/p/11779513.html