2017 ACM-ICPC, Universidad Nacional de Colombia Programming Contest gym101466 题解

时间:2020-04-17
本文章向大家介绍2017 ACM-ICPC, Universidad Nacional de Colombia Programming Contest gym101466 题解,主要包括2017 ACM-ICPC, Universidad Nacional de Colombia Programming Contest gym101466 题解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

A. 给你n个不超过1e18的数字,n的数量级是1e6。从中选出两个数,做无进位加法,问最大的数能多大。

  sol:将每个数字补成相同长度,除了第一个数字外,每一个数只需要在字典树上贪心的去跑就行了。查询完之后,将这个数字插入到字典树中。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline LL read(){
    LL res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 20'000'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}

int T[MAXN][10], tol;

LL pw[25];

void Insert(LL x){
    vector<int> arr;
    while(SZ(arr)<20){
        arr.push_back(x%10);
        x/=10;
    }
    reverse(all(arr));

    int now=0;
    for(int i=0;i<20;++i){
        if(!T[now][arr[i]])T[now][arr[i]]=++tol;
        now=T[now][arr[i]];
    }
}

LL queryMin(LL x){
    LL res=0;
    vector<LL> arr;

    while(SZ(arr)<20){
        arr.push_back(x%10);
        x/=10;
    }

    reverse(all(arr));

    int now=0;
    for(int i=0;i<20;++i){
        LL nd=0;
        LL mn=11;
        for(int j=0;j<10;++j){
            if(T[now][j]){
                if((arr[i]+j)%10<mn){
                    mn=(arr[i]+j)%10;
                    nd=T[now][j];
                }
            }
        }

        res+=mn*pw[19-i];
        now=nd;
    }
    return res;
}

LL queryMax(LL x){
    LL res=0;
    vector<LL> arr;

    while(SZ(arr)<20){
        arr.push_back(x%10);
        x/=10;
    }

    reverse(all(arr));

    int now=0;
    for(int i=0;i<20;++i){
        LL nd=0;
        LL mx=-1;
        for(int j=0;j<10;++j){
            if(T[now][j]){
                if((arr[i]+j)%10>mx){
                    mx=(arr[i]+j)%10;
                    nd=T[now][j];
                }
            }
        }
        res+=mx*pw[19-i];
        now=nd;
    }

    return res;
}

LL n;

int main(){
    pw[0]=1;
    for(int i=1;i<=18;++i)pw[i]=pw[i-1]*10ll;

    n=read();
    LL ans1=(LL)1e19, ans2=0;//最大值不够大wa
    for(int i=1;i<=n;++i){
        LL x=read();
        if(i>1){
            ans1=min(ans1,queryMin(x));
            ans2=max(ans2,queryMax(x));
        }
        Insert(x);
    }

    cout<<ans1<<" "<<ans2<<endl;
    return 0;
}

/*
2
1000000000000000000 1000000000000000000
*/
View Code

B. 给你n个数字,表示你可以选择一个深度,这个深度每一个节点的儿子节点个数为这n个数中的一个,问最多能有多少个节点。

  sol:显然从大到小排序,算一下个数就行了。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline LL read(){
    LL res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 200'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}


LL n;
LL A[50];

int main(){
    n=read();
    for(int i=1;i<=n;++i)A[i]=read();
    sort(A+1,A+1+n);

    LL ans=1;
    LL t=1;
    for(int i=n;i>=1;--i){
        ans+=t*A[i];
        t*=A[i];
    }

    cout<<ans;

    return 0;
}
View Code

C. 给定三维空间n个点,第一个点为中心点,要将中心点与其他点连线,形成一条一条直线。使得除中心点以外的其他点至少被一条直线覆盖。最少需要连多少条直线?

  sol:从第二个点开始,与中心点连线。并且将其他共线的点打上标记,判断共线就用点积就行。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 5'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}


struct Node{
    LL x, y, z;
    Node(){}
    Node(LL _x, LL _y, LL _z){
        x=_x;y=_y;z=_z;
    }
    Node operator-(const Node& a){
        return Node(x-a.x,y-a.y,z-a.z);
    }
}points[MAXN];

LL n;

int vis[MAXN];

LL Dot(Node a, Node b){
    return a.x*b.x+a.y*b.y+a.z*b.z;
}

int main(){
    n=read();
    for(int i=1;i<=n;++i){
        points[i].x=read();
        points[i].y=read();
        points[i].z=read();
    }

    int ans=0;

    for(int i=2;i<=n;++i){
        if(vis[i])continue;
        else{
            ++ans;
            for(int j=i+1;j<=n;++j){
                LL t=Dot(points[i]-points[1],points[j]-points[1]);
                if(t*t==Dot(points[i]-points[1],points[i]-points[1])*Dot(points[j]-points[1],points[j]-points[1])){
                    vis[j]=1;
                }
            }
        }
    }

    cout<<ans<<endl;
    return 0;
}
View Code

D. 初始时,你有一个数,值为0,你可以进行两种操作。第一种:将这个值变成2*x+1,第二种,将这个值变成2*x+2。问经过多少次操作可以将0变成一个指定的值。

  sol:由于两种操作出来的值奇偶性不同,注意到这个,就可以进行逆推了。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 200'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}


int n;

int main(){
    n=read();

    string ans;
    while(n){
        if(n&1){
            ans+="A";
            --n;
            n/=2;
        }else{
            ans+="B";
            n-=2;
            n/=2;
        }
    }

    reverse(all(ans));
    cout<<ans;
    return 0;
}
View Code

E. 给一个文本串,和一个模式串和一个整数k。问这个模式串在文本串中出现的次数>=k的最长前缀。

  sol:先跑kmp的前置,得到fail指针。然后开始跑匹配的时候,可以搞出每一个文本串前缀,所匹配模式串的最大前缀,而每一个模式串的前缀,对其fail指针指向的模式串前缀是有贡献的(之所以不是所有前缀是因为,这样搞会算重)。我们跑完之后,倒着扫一遍我们的计数数组,当个数大于等于k的时候,得到答案,否则给其对应fail指针的位置加上贡献。

  sol2:注意到单调性,就可以二分答案,逐个check。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 200'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}

char s[MAXN];

char pat[MAXN];

int Next[MAXN];

int cnt[MAXN];

void getNext(char* str){
    int len=strlen(str+1);
    Next[1]=0;
    int j=0;

    for(int i=2;i<=len;++i){
        while(j&&str[i]!=str[j+1])j=Next[j];
        if(str[i]==str[j+1])++j;
        Next[i]=j;
    }
}

void Match(char* s1, char* s2){
    getNext(s2);
    int len1=strlen(s1+1);
    int len2=strlen(s2+1);
    int j=0;
    for(int i=1;i<=len1;++i){
        while(j&&s1[i]!=s2[j+1])j=Next[j];
        if(s1[i]==s2[j+1])++j;
        ++cnt[j];
//        cout<<j<<endl;
        if(j==len2)j=Next[j];
    }
}

int k;

int main(){
    gets(s+1);
    gets(pat+1);
    k=read();

    int len1=strlen(s+1);
    int len2=strlen(pat+1);

    Match(s, pat);
    int ans=-1;
    for(int i=len2;i>=1;--i){
        if(cnt[i]>=k){
            ans=i;
            break;
        }else{
            cnt[Next[i]]+=cnt[i];
        }
    }
    if(ans==-1)cout<<"IMPOSSIBLE";
    else{
        for(int i=1;i<=ans;++i)cout<<pat[i];
    }

    return 0;
}
View Code

F. 判一下给的三个数能不能构成三角形。

  sol:两边之和大于第三边。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 200'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}


int n;

int main(){
    n=read();
    int flag=1;
    for(int i=1;i<=n;++i){
        vector<int> t(3);
        for(int j=0;j<3;++j)t[j]=read();
        sort(all(t));
        if(t[0]+t[1]<=t[2])flag=0;
    }

    if(flag)cout<<"YES"<<endl;
    else cout<<"NO"<<endl;

    return 0;
}
View Code

G. 给你一些正整数(2<=x<=1e6),首先你要将这些数质因数分解。并且将这个质因数拼接成一个字符串,现在允许两种操作,第一是从这个字符串中取出一个数字(1<=num<=9),换成一个字母(a=1,b=2,...)。拼到一个目标字符串的后面。第二是取出两个数字,拼成一个数字。这个数字显然在(10<=num<=26)之间,同样是换成对应的字母。拼接到目标字符串的后面。问我们最终能拼成的目标字符串的种类数。

  sol:首先我们有一个观察,由于x<=1e6,所以在质因数分解的过程中,拼成的字符串长度<=20。为什么这么说呢?我们考虑质因数2,长度占1,如果全是2,1e6也最多20个。对于其他的长度占1的质因数而言,贡献来的大,代价却不变,显然不会超过20个。考虑其他两位数,三位数的质因数呢?由于两个数的质因数,代价是2,贡献大于4,不会超过20位,其他同理。那么既然第一个字符串长度不超过20,这个题就变得极容易了,直接考虑状压dp,记忆化搜索就能通过了。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 200'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T& a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T& a, T b){if(b>a)a=b;}

int dp[(1<<20)+5];

vector<int> a;

int sz;

int dfs(int state){
    if(dp[state]!=-1)return dp[state];

    if(state==0) return 1;

    int& ret=dp[state];
    ret=0;

    int vis[27]={};

    for(int i=0;i<sz;++i){
        if((state&(1<<i))&&a[i]!=0){
            if(!vis[a[i]]){
                vis[a[i]]=1;
                addmod(ret,dfs(state^(1<<i)));
            }

            for(int j=0;j<sz;++j){
                if(i!=j&&(state&(1<<j))){
                    int x=a[i]*10+a[j];
                    if(x>=10&&x<=26&&!vis[x]){
                        vis[x]=1;
                        addmod(ret,dfs(state^(1<<i)^(1<<j)));
                    }
                }
            }
        }
    }

    return ret;
}

int main(){
    int n;
    while(cin>>n){
        memset(dp,-1,sizeof dp);
        a.clear();
        int nn=n;
        for(int i=2;i*i<=nn;++i){
            while(n%i==0){
                int ii=i;
                while(ii>0){
                    a.PB(ii%10);
                    ii/=10;
                }
                n/=i;
            }
        }

        if(n>2){
            while(n>0){
                a.PB(n%10);
                n/=10;
            }
        }

        sz=SZ(a);

        cout<<dfs((1<<sz)-1)<<endl;
    }

    return 0;
}
View Code

H. C语言期末考试题(逃。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 200'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}

int m;

int main(){
    m=read();
    for(int i=1;i<=m;++i){
        if(i==1){
            cout<<"*";
            for(int j=1;j<=m-2;++j)cout<<" ";
            cout<<"* ";
            for(int j=1;j<=m;++j)cout<<"*";
            cout<<endl;
        }else if(i==m){
            for(int j=1;j<=m;++j)cout<<"*";
            cout<<" ";
            cout<<"*";
            for(int j=1;j<=m-2;++j)cout<<" ";
            cout<<"*";
        }else{
            cout<<"*";
            for(int j=1;j<=m-2;++j)cout<<" ";
            cout<<"* ";
            cout<<"*";
            for(int j=1;j<=m-2;++j)cout<<" ";
            cout<<"*";
            cout<<endl;
        }

    }

    return 0;
}
View Code

I. 给你一个数组,你要将这个数组分成两个组,第一组的数字全都是回文串,第二组的数字里面只有4和7。问你能不能存在一种方法,使得你的分组方式,能够使得每一组的数字在原数组中距离相差不超过20。

  sol:题目还是很直观的,我们显然要设计一个dp状态,dp[cur][x1][x2][s1][s2],表示当前处理到了第cur个,上一个第一组的与前一个的距离,上一个第二组的与前一个的距离,第一组的最后一个数是否有邻居,第二组的最后一个数是否有邻居(两个标志位维度是为了更好处理每组第一个)是否能分组。那么问题就来了,首先是x1,x2,要是直接开1e5,那显然会MLE(建议放到太湖之光上跑)。考虑到距离大于20的都可以认为是21,能减小空间复杂度。但是可惜的是,即使我们把dp[cur][x1][x2][s1][s2]换成bool型还是会MLE。所以我们这个时候换成bitset就可以通过(出题人老毒瘤了)。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 100'010;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}

//两组分别看的话,只有这一组里前一个比较重要
//为了解决第一个的问题,引入了标记

bitset<MAXN> dp[22][22][2][2];
bitset<MAXN> vis[22][22][2][2];

int mark[MAXN];

int n, k;

bool check1(int x){
    vector<int> arr;
    while(x){
        arr.push_back(x%10);
        x/=10;
    }
    int sz=SZ(arr);

    for(int i=0;i<sz/2;++i)
        if(arr[i]!=arr[sz-i-1])return false;

    return true;
}

bool check2(int x){
    while(x){
        if(x%10!=4&&x%10!=7)return false;
        x/=10;
    }

    return true;
}

bool dfs(int cur, int d1,int d2, int isOk1, int isOk2){
    if(vis[d1][d2][isOk1][isOk2][cur])return dp[d1][d2][isOk1][isOk2][cur];

    if(cur==n+1){
        if(!isOk1||!isOk2)return 0;
        else return 1;
    }

    vis[d1][d2][isOk1][isOk2][cur]=1;

    bool ret=0;

    if(mark[cur]&1){
        if(d1!=k+1||isOk1){
            ret|=dfs(cur+1,1,min(k+1,d2+1),d1<=k?1:0,isOk2);
        }
    }

    if(mark[cur]&2){
        if(d2!=k+1||isOk2){
            ret|=dfs(cur+1,min(k+1,d1+1),1,isOk1,d2<=k?1:0);
        }
    }

    return dp[d1][d2][isOk1][isOk2][cur]=ret;

}

int main(){
    n=read();k=read();
    for(int i=1;i<=n;++i){
        int x=read();
        if(check1(x))mark[i]|=1;
        if(check2(x))mark[i]|=2;
        if(mark[i]==0){
            cout<<"NO";
            return 0;
        }
    }

    int ok=dfs(1,k+1,k+1,1,1);
    if(ok)cout<<"Yes";
    else cout<<"No";

    return 0;
}
View Code

J. 先给你m个数,要组成长度为n的数组。i>m时a[i]=(a[i-m]+a[i-m+1])%mod。问你把这些数升序排列之后,q此询问,每次询问ranki。

  sol:模拟即可。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 30'000'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}


int cnt[MAXN];
int n, m, q;

int A[MAXN];

int main(){
    n=read();m=read();q=read();
    for(int i=1;i<=m;++i)A[i]=read(),++cnt[A[i]];

    for(int i=m+1;i<=n;++i){
        A[i]=(A[i-m]+A[i-m+1])%30000000;
        ++cnt[A[i]];
    }

    for(int i=1;i<=30000000;++i)cnt[i]=cnt[i]+cnt[i-1];

    for(int i=1;i<=q;++i){
        int x=read();
        int ans=lower_bound(cnt,cnt+30000000,x)-cnt;
        cout<<ans<<endl;
    }

    return 0;
}
View Code

K. 给你一颗树,有n个点,每一个点有点权。现在给出两种操作,第一是查询一个节点子树的所有点权乘积的因子数。第二个将树上某一个节点的值乘上val。题目保证所有数的最大素因子不超过13。

  sol:子树问题当然要dfs序,考虑到最大素因子不超过13,写6颗线段树就能通过了,代码量稍大,但也是送分题。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 200'005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T a, T b){if(b>a)a=b;}

#define go(e,u) for(int e=head[u];e;e=Next[e])
int to[MAXN<<1],Next[MAXN<<1],head[MAXN],tol;

void add_edge(int u,int v){
    Next[++tol]=head[u];to[tol]=v;head[u]=tol;
    Next[++tol]=head[v];to[tol]=u;head[v]=tol;
}

int arr[]={2,3,5,7,11,13};

inline int getid(int x){return lower_bound(arr,arr+7,x)-arr;}

int n;

int L[MAXN], R[MAXN], dfn, id[MAXN];

int P[MAXN<<2];

int T[MAXN<<2][6];

int val[MAXN];

void dfs(int u,int f){
    L[u]=++dfn;
    id[dfn]=u;

    go(e,u){
        int v=to[e];
        if(v==f)continue;
        dfs(v,u);
    }

    R[u]=dfn;
}


void build1(int nd, int l, int r){
    if(l==r){
        P[nd]=val[id[l]];
        return;
    }

    build1(lson, l, mid);
    build1(rson, mid+1, r);

    P[nd]=1ll*P[lson]*P[rson]%MOD;
}

void update1(int nd, int l, int r, int pos, int v){
    if(l==r){
        P[nd]=1ll*P[nd]*v%MOD;
        return;
    }

    if(pos<=mid)update1(lson, l, mid, pos, v);
    else update1(rson, mid+1, r, pos, v);

    P[nd]=1ll*P[lson]*P[rson]%MOD;
}

int query1(int nd, int l, int r, int L, int R){
    if(L<=l&&r<=R){
        return P[nd];
    }

    int res=1;
    if(L<=mid)res=1ll*res*query1(lson, l, mid, L, R)%MOD;
    if(R>=mid+1)res=1ll*res*query1(rson, mid+1, r, L, R)%MOD;

    return res;
}

void build2(int nd, int l, int r){
    if(l==r){
        int x=val[id[l]];
        for(int i=0;i<6;++i){
            int num=0;
            while(x%arr[i]==0){
                ++num;
                x/=arr[i];
            }
            T[nd][i]+=num;
        }
        return;
    }

    build2(lson,l,mid);
    build2(rson,mid+1,r);

    for(int i=0;i<6;++i)T[nd][i]=T[lson][i]+T[rson][i];
}

void update2(int nd, int l, int r, int pos, int v){
    if(l==r){
        int x=v;
        for(int i=0;i<6;++i){
            int num=0;
            while(x%arr[i]==0){
                ++num;
                x/=arr[i];
            }
            T[nd][i]+=num;
        }
        return;
    }

    if(pos<=mid)update2(lson,l,mid,pos,v);
    else update2(rson,mid+1,r,pos,v);

    for(int i=0;i<6;++i)T[nd][i]=T[lson][i]+T[rson][i];
}

vector<int> query2(int nd, int l, int r, int L, int R){
    if(L<=l&&r<=R){
        vector<int> res(6,0);
        for(int i=0;i<6;++i)res[i]=T[nd][i];
        return res;
    }

    vector<int> res(6,0);
    if(L<=mid){
        vector<int> t;
        t=query2(lson,l,mid,L,R);
        for(int i=0;i<6;++i)res[i]+=t[i];
    }

    if(mid+1<=R){
        vector<int>t;
        t=query2(rson,mid+1,r,L,R);
        for(int i=0;i<6;++i)res[i]+=t[i];
    }

    return res;
}

int main(){
    n=read();
    for(int i=1;i<n;++i){
        int u, v;
        u=read();v=read();
        ++u,++v;
        add_edge(u,v);
    }
    dfs(1,0);

    for(int i=1;i<=n;++i)val[i]=read();
    build1(1,1,n);
    build2(1,1,n);

    int q;
    q=read();
    while(q--){
        char buf[10];
        scanf("%s",buf+1);
        if(buf[1]=='R'){
            int w=read();
            ++w;
            int lb=L[w],rb=R[w];
            int ans=query1(1,1,n,lb,rb);
            vector<int> res=query2(1,1,n,lb,rb);
            cout<<ans<<" ";
            ans=1;
            for(int i=0;i<6;++i)ans=1ll*ans*(1+res[i])%MOD;
            cout<<ans<<endl;
        }else{
            int p=read();
            ++p;
            p=L[p];
            int v=read();
            update1(1,1,n,p,v);
            update2(1,1,n,p,v);
        }
    }

    return 0;
}
View Code

原文地址:https://www.cnblogs.com/JohnRan/p/12719856.html