2020 Jiangsu Collegiate Programming Contest题解

A题

线段树+欧拉降幂

题目要求的状态只有30种,所以对于每次操作,我们可以枚举所有的情况,计算他的转移方向,我们用lazy标记表示这个数操作完后的状态,打个懒标记

然后对于询问的时候,只要询问每个数的个数即可。对于快速幂的情况,普通快速幂因为太慢,所以考虑使用欧拉降幂来加速

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=2e5+10;
const int mod=1e9+7;
struct node{
    int l,r;
    int sum[33];
    int lazy[33];
}tr[N<<2];
int n,p;
int cnt[33],num[33];
int a[N];
int phi;
int eular(int n){
    int ans = n;
    for(int i=2;i*i<=n;++i){
        if(n%i == 0){
            ans = ans/i * (i-1);
            while(n % i == 0){
                n /= i;
            }
        }
    }
    if(n > 1) ans = ans / n * (n - 1);
    return ans;
}
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int qmi(int a,int b){
       ll ans=1;
       if(gcd(a,p) == 1){
          b=b%phi;
       }
       else if(b>=phi){
            b=b%phi+phi;
       }
       while(b>0){
            if(b&1) ans=ans*a%p;
            a=a*a%p;
            b>>=1;
       }
       return ans;
}
void pushup(int u){
    for(int i=0;i<p;i++){
        tr[u].sum[i]=tr[u<<1].sum[i]+tr[u<<1|1].sum[i];
    }
}
void build(int u,int l,int r){
    if(l==r){
        tr[u]={l,r};
        for(int i=0;i<p;i++){
            tr[u].lazy[i]=i;
        }
        tr[u].sum[a[l]%p]++;
    }
    else{
        tr[u]={l,r};
        for(int i=0;i<p;i++){
            tr[u].lazy[i]=i;
        }
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
        pushup(u);
    }
}
int get(int opt,int i,int l){
    if(opt==1){
        return 1ll*(i+l)%p;
    }
    else if(opt==2){
        return 1ll*i*l%p;
    }
    else{
        return 1ll*(qmi(i,l))%p;
    }
}
void pushdown(int u){
    int i;
    for(i=0;i<p;i++){
        num[i]=tr[u<<1].sum[i];
        tr[u<<1].sum[i]=0;
    }
    for(i=0;i<p;i++){
        tr[u<<1].sum[tr[u].lazy[i]]+=num[i];
    }
    for(i=0;i<p;i++){
        num[i]=tr[u<<1|1].sum[i];
        tr[u<<1|1].sum[i]=0;
    }
    for(i=0;i<p;i++){
        tr[u<<1|1].sum[tr[u].lazy[i]]+=num[i];
    }
    for(i=0;i<p;i++){
        tr[u<<1].lazy[i]=tr[u].lazy[tr[u<<1].lazy[i]];
        tr[u<<1|1].lazy[i]=tr[u].lazy[tr[u<<1|1].lazy[i]];
    }
    for(i=0;i<p;i++){
        tr[u].lazy[i]=i;
    }
}
void modify(int u,int l,int r,int x,int opt){
    if(tr[u].l>=l&&tr[u].r<=r){
        int i;
        for(i=0;i<p;i++){
            num[i]=tr[u].sum[i];
            tr[u].sum[i]=0;
        }
        for(i=0;i<p;i++){
            int pos=get(opt,i,x);
            tr[u].lazy[i]=get(opt,tr[u].lazy[i],x);
            tr[u].sum[pos]+=num[i];
        }
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid)
        modify(u<<1,l,r,x,opt);
    if(r>mid)
        modify(u<<1|1,l,r,x,opt);
    pushup(u);
}
void query(int u,int l,int r){
    if(tr[u].l>=l&&tr[u].r<=r){
        int i;
        for(i=0;i<p;i++){
            cnt[i]+=tr[u].sum[i];
        }
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid)
        query(u<<1,l,r);
    if(r>mid)
        query(u<<1|1,l,r);
}
int main(){
    ios::sync_with_stdio(false);
    int i;
    cin>>n>>p;
    phi=eular(p);
    for(i=1;i<=n;i++)
        cin>>a[i];
    build(1,1,n);
    int q;
    cin>>q;
    while(q--){
        int opt,l,r,k;
        cin>>opt>>l>>r>>k;
        if(opt<=3){
            modify(1,l,r,k,opt);
        }
        else if(opt==4){
            memset(cnt,0,sizeof cnt);
            int ans=0;
            query(1,l,r);
            for(i=0;i<p;i++){
                ans=(ans+qmi(i,k)*cnt[i])%p;
            }
            cout<<ans<<endl;
        }
        else{
            memset(cnt,0,sizeof cnt);
            int ans=1;
            query(1,l,r);
            for(i=0;i<p;i++){
                ans=ans*qmi(i,cnt[i])%p;
            }
            cout<<ans<<endl;
        }
    }
    return 0;
}
View Code

C题

观察到,对于每个数,都放置在比他小1的数的两边就是一种 合法方案,那么这种方案其实就是二叉树的中序遍历

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=5e6+10;
const int mod=1e9+7;
int l[N],r[N],w[N];
int cnt;
vector<int> num;
void dfs(int u,int sum){
    w[u]=sum;
    if(sum==20)
        return ;
    l[u]=u<<1,r[u]=u<<1|1;
    dfs(u<<1,sum+1);
    dfs(u<<1|1,sum+1);
}
void get(int u){
    if(l[u])
        get(l[u]);
    num.push_back(w[u]);
    if(r[u])
        get(r[u]);
}
int main(){
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    int i;
    dfs(1,1);
    get(1);
    for(i=1;i<=n;i++)
        cout<<num[i]<<" ";
    cout<<endl;
}
View Code

D题

线段树+思维

首先要想到小范围的答案不会影响大范围,因此考虑先对操作离线排序。对于下一步

我们想到,如果直到这个数在第几组删除,并且是这个组的第几个那么就能知道全部答案

这步操作,可以通过线段树维护,因为每个数只有变成1或者变成当前位置是质数的时候。

这样只要维护每一轮删除的数,之后从第0轮往上增即可,因为轮数不会太多,所以复杂度过关

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=1e6+10;
const int mod=1e9+7;
struct node{
    int l,r;
    int sum;
}tr[N<<2];
struct query{
    int op,n,k,id;
}s[N];
int num[N],pos[N];
vector<int> g[N];
int st[N],primes[N];
int ans[N];
bool cmp(query a,query b){
    return a.n<b.n;
}
void build(int u,int l,int r){
    if(l==r){
        tr[u]={l,r};
    }
    else{
        tr[u]={l,r};
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
    }
}
void pushup(int u){
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void modify(int u,int l,int r,int p,int x){
    if(tr[u].l==tr[u].r){
        tr[u].sum+=x;
        return ;
    }
    int mid=tr[u].l+tr[u].r>>1;
    if(p<=mid)
        modify(u<<1,l,r,p,x);
    else
        modify(u<<1|1,l,r,p,x);
    pushup(u);
}
int query(int u,int l,int r,int x){
    if(x==0)
        return 0;
    if(tr[u].l==tr[u].r){
        return tr[u].sum;
    }
    int mid=tr[u].l+tr[u].r>>1;
    if(x<=mid){
        return query(u<<1,l,r,x);
    }
    else{
        return tr[u<<1].sum+query(u<<1|1,l,r,x);
    }
}
int get(int u,int l,int r,int x){
    if(tr[u].l==tr[u].r){
        return tr[u].l;
    }
    int mid=tr[u].l+tr[u].r>>1;
    if(x<=tr[u<<1].sum){
        return get(u<<1,l,r,x);
    }
    else{
        return get(u<<1|1,l,r,x-tr[u<<1].sum);
    }
}
int cnt;
void init(int mx){
    int i;
    for(i=2;i<=mx;i++){
        if(!st[i]){
            primes[++cnt]=i;
        }
        for(int j=1;primes[j]*i<=mx;j++){
            st[i*primes[j]]=1;
            if(i%primes[j]==0){
                break;
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    int i;
    int mx=0;
    for(i=1;i<=t;i++){
        cin>>s[i].op>>s[i].n>>s[i].k;
        s[i].id=i;
        mx=max(mx,s[i].n);
    }
    sort(s+1,s+1+t,cmp);
    int now=1;
    build(1,1,1000);
    init(mx);
    for(i=1;i<=mx;i++){
        if(i==1){
            modify(1,1,1000,1,1);
            num[i]=1;
            pos[i]=1;
            g[1].push_back(1);
        }
        else{
            int turn=0;
            while(1){
                int tmp=query(1,1,1000,turn);
                if(i-tmp==1||!st[i-tmp]){
                    break;
                }
                turn++;
            }
            num[i]=turn+1;
            modify(1,1,1000,turn+1,1);
            g[turn+1].push_back(i);
            pos[i]=g[turn+1].size();
        }
        while(now<=t&&s[now].n==i){
            if(s[now].op==1){
                int tmpsum=query(1,1,1000,num[s[now].k]-1);
                tmpsum+=pos[s[now].k];
                ans[s[now].id]=tmpsum;
            }
            else{
                int tmpsum=get(1,1,1000,s[now].k);
                int res=s[now].k-query(1,1,1000,tmpsum-1);
                ans[s[now].id]=g[tmpsum][res-1];
            }
            now++;
        }
    }
    for(i=1;i<=t;i++)
        cout<<ans[i]<<endl;
    return 0;
}
View Code

H题

简单dp题,因为发现数据不会很大,因此直接暴力dp匹配即可,注意判断可能模完后变成0和1的情况

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
typedef pair<string,int> pss;
const int N=1e5+10;
const int mod=1e9+7;
ll f[N];
map<string,int> m1;
string s;
ll f1[N];
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--){
        m1.clear();
        memset(f,0,sizeof f);
        memset(f1,0,sizeof f1);
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=m;i++){
            string a,b;
            cin>>a>>b;
            m1[b]++;
        }
        cin>>s;
        s=" "+s;
        f[0]=1;
        f1[0]=1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=5&&j<=i;j++){
                string d=s.substr(i-j+1,j);
                if(m1[d]){
                    f[i]=(f[i]+f[i-j]*m1[d]%128)%128;
                    f1[i]=(f1[i]+f1[i-j]*m1[d]%mod)%mod;
                }
            }
        }
        if(f[n]==1&&f1[n]==1){
            cout<<"happymorsecode"<<endl;
        }
        else if(!f[n]&&!f1[n]){
            cout<<"nonono"<<endl;
        }
        else{
            cout<<"puppymousecat "<<f[n]<<endl;
        }
    }
}
View Code

I题

最短路+思维

注意到这个图是边权不断在变的正权图,因此依旧满足迪杰斯特拉的贪心法则。

所以我们依旧跑最短路,这依然是正解,只不过要根据时间加两条不同的边。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
typedef pair<string,int> pss;
const int N=1e5+10;
const int mod=1e9+7;
int sx,sy,ex,ey;
int n,m;
ll a[505][505],b[505][505];
ll c[505][505],d[505][505];
ll dis[5050][505];
int st[505][505];
struct node{
    int x,y;
    ll dis;
    bool operator <(const node &t) const{
        return dis>t.dis;
    }
};
void dij(){
    memset(dis,0x3f,sizeof dis);
    dis[sx][sy]=0;
    priority_queue<node> q;
    q.push({sx,sy,0});
    while(q.size()){
        auto t=q.top();
        q.pop();
        if(st[t.x][t.y])
            continue;
        st[t.x][t.y]=1;
        ll lim=t.dis%(a[t.x][t.y]+b[t.x][t.y]);
        int x=t.x,y=t.y;
        if(x>1){
            if(lim<a[x][y]){
                if(dis[x-1][y]>dis[x][y]+d[x-1][y]){
                    dis[x-1][y]=dis[x][y]+d[x-1][y];
                    q.push({x-1,y,dis[x-1][y]});
                }
            }
            else{
                ll tmp=a[x][y]+b[x][y]-lim;
                if(dis[x-1][y]>dis[x][y]+d[x-1][y]+tmp){
                    dis[x-1][y]=dis[x][y]+d[x-1][y]+tmp;
                    q.push({x-1,y,dis[x-1][y]});
                }
            }
        }
        if(x<n){
            if(lim<a[x][y]){
                if(dis[x+1][y]>dis[x][y]+d[x][y]){
                    dis[x+1][y]=dis[x][y]+d[x][y];
                    q.push({x+1,y,dis[x+1][y]});
                }
            }
            else{
                ll tmp=a[x][y]+b[x][y]-lim;
                if(dis[x+1][y]>dis[x][y]+d[x][y]+tmp){
                    dis[x+1][y]=dis[x][y]+d[x][y]+tmp;
                    q.push({x+1,y,dis[x+1][y]});
                }
            }
        }
        if(y>1){
            if(lim>=a[x][y]){
                if(dis[x][y-1]>dis[x][y]+c[x][y-1]){
                    dis[x][y-1]=dis[x][y]+c[x][y-1];
                    q.push({x,y-1,dis[x][y-1]});
                }
            }
            else{
                ll tmp=a[x][y]-lim;
                if(dis[x][y-1]>dis[x][y]+c[x][y-1]+tmp){
                    dis[x][y-1]=dis[x][y]+c[x][y-1]+tmp;
                    q.push({x,y-1,dis[x][y-1]});
                }
            }
        }
        if(y<m){
            if(lim>=a[x][y]){
                //cout<<"gg"<<endl;
                if(dis[x][y+1]>dis[x][y]+c[x][y]){
                    //cout<<"hh"<<endl;
                    dis[x][y+1]=dis[x][y]+c[x][y];
                    q.push({x,y+1,dis[x][y+1]});
                }
            }
            else{
                ll tmp=a[x][y]-lim;
                if(dis[x][y+1]>dis[x][y]+c[x][y]+tmp){
                    dis[x][y+1]=dis[x][y]+c[x][y]+tmp;
                    q.push({x,y+1,dis[x][y+1]});
                }
            }
        }
    }
    cout<<dis[ex][ey]<<endl;
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m>>sx>>sy>>ex>>ey;
    int i,j;
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            cin>>a[i][j];
        }
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            cin>>b[i][j];
        }
    }
    for(i=1;i<=n;i++){
        for(j=1;j<m;j++)
            cin>>c[i][j];
    }
    for(i=1;i<n;i++){
        for(j=1;j<=m;j++)
            cin>>d[i][j];
    }
    dij();
}
View Code

J题

队友写的

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define LL long long
#define ull unsigned long long
#define PI acos(-1.0)
#define eps 1e-12
#define fi first
#define se second
#define MEM(a,b) memset((a),(b),sizeof(a))
#define mod(x) ((x)%MOD)
#define wz cout<<"-----"<<endl;
#define pb push_back
#define mp make_pair
#define pll pair <LL, LL>
#define pii pair <int, int>
#define rep(i,x) for(int i=1;i<=x;i++)
const int INF_INT = 2147483647;
const ll INF_LL = 9223372036854775807LL;
const ull INF_ULL = 18446744073709551615Ull;
const ll P = 92540646808111039LL;
 
const ll maxn = 1e5 + 10, MOD = 1e9 + 7;
const int Move[4][2] = {-1,0,1,0,0,1,0,-1};
const int Move_[8][2] = {-1,-1,-1,0,-1,1,0,-1,0,1,1,-1,1,0,1,1};
 
inline 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 tx[1000033];
int y;
int g(int x){
    if(tx[x])return tx[x];
    else return tx[x]=1+g(y%x);
}
int main(){
    int t=read();
    while(t--){
        y=read();
        if(y==2){
            printf("%.10lf\n",1.0);
            continue;
        }
        ll sum=0;
        tx[1]=1;
        for(int i=1;i<=y/2;i++){
            sum+=g(i);
        }
        sum=2*sum+y/2;
        printf("%.10lf\n",sum*1.0/(y-1));
        for(int i=1;i<y;i++)tx[i]=0;
    }
}
View Code

 

posted @ 2021-04-08 21:53  朝暮不思  阅读(398)  评论(0编辑  收藏  举报