BZOJ 2018 五月月赛

A
题目大意

给定一列数a,多组询问 d i | j = L i R j a j 是否成立。多组数据。 n , a , q 1 e 5

题解

考虑预处理每个数的质因子及其指数,然后将询问离线,一边维护cnt前缀和数组,一边更新答案(其实就是把主席树离线下来)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<assert.h>
#include<utility>
#include<vector>
#define gc getchar()
#define lint long long
#define N 100010
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int notp[N],pri[N],pc,a[N],cnt[N];
vector<pii> p[N],q[N];int ds[N];
vector<int> ans[N];
int main()
{
    int n=100000;
    for(int i=2;i<=n;i++)
    {
        if(!notp[i]) pri[++pc]=i;
        for(int j=1;j<=pc&&(lint)i*pri[j]<=n;j++)
        {
            notp[i*pri[j]]=true;
            if(i%pri[j]==0) break;
        }
    }
    for(int i=1;i<=pc;i++)
        for(int j=pri[i];j<=n;j+=pri[i])
        {
            int x=j,cp=0;
            while(x%pri[i]==0) x/=pri[i],cp++;
            p[j].push_back(make_pair(pri[i],cp));
        }
    for(int T=inn();T;T--)
    {
        int n=inn(),m=inn();
        for(int i=1;i<=n;i++) a[i]=inn();
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++) q[i].clear();
        for(int i=1,l,r;i<=m;i++)
            l=inn(),r=inn(),ds[i]=inn(),
            ans[i].clear(),ans[i].resize(p[ds[i]].size()),
            q[l-1].pb(mp(i,-1)),q[r].pb(mp(i,1));
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<(int)p[a[i]].size();j++)
                cnt[p[a[i]][j].fir]+=p[a[i]][j].sec;
            for(int j=0;j<(int)q[i].size();j++)
            {
                int id=q[i][j].fir,d=ds[id],sgn=q[i][j].sec;
                for(int k=0;k<(int)p[d].size();k++)
                    ans[id][k]+=sgn*cnt[p[d][k].fir];
            }
        }
        for(int i=1;i<=m;i++)
        {
            int ok=1;
//          debug(ds[i])ln;
//          for(int j=0;j<(int)p[ds[i]].size();j++)
//              debug(j)sp,debug(p[ds[i]][j].fir)sp,debug(p[ds[i]][j].sec)ln;
//          for(int j=0;j<(int)ans[i].size();j++)
//              debug(j)sp,debug(ans[i][j])ln;
            for(int j=0;j<(int)ans[i].size();j++)
                if(ans[i][j]<p[ds[i]][j].sec) { ok=0;break; }
            printf("%s\n",ok?"Yes":"No");
        }
    }
    return 0;
}
B
题目大意

给你一个网格图,每个格子有一个正权值,你可以任意交换两个数字k次,然后从左上角走到右下角,问最大点权之和。 n , m 50 , k 20

题解

考虑dp,令dp[i][j][x][y]表示走到(i,j),路径上有x个格子没有统计入答案,路径外有y个格子计入答案,转移有两种,要么向右走并考虑x是否+1,要么向下走并把第i行第j+1~n的位置和第i+1行1~j-1的位置的并集的前t大计入答案,以及考虑是否把(i+1,j)计入答案。复杂度 O ( n 2 k 3 )
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#define gc getchar()
#define N 55
#define K 25
#define inf (INT_MIN/10)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int upd(int &x,int y) { return x=max(x,y); }
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int a[N][N],b[N],ps[N][N][N],dp[N][N][K][K];
int main()
{
    for(int T=inn();T;T--)
    {
        int n=inn(),m=inn(),k=min(inn(),n+m-1);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) a[i][j]=inn();
        for(int i=2;i<=n;i++)
            for(int j=1,c;j<=m;j++)
            {
                c=0;for(int t=1;t<j;t++) b[++c]=a[i][t];
                for(int t=j+1;t<=m;t++) b[++c]=a[i-1][t];
                sort(b+1,b+m);for(int t=1;t<=(m-1)/2;t++) swap(b[t],b[m-t]);
                for(int t=1;t<m;t++) ps[i][j][t]=ps[i][j][t-1]+b[t];//,debug(i)sp,debug(j)sp,debug(t)sp,debug(ps[i][j][t])ln;
            }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                for(int x=0;x<=min(i+j-1,k);x++)
                    for(int y=0;y<=k;y++)
                        dp[i][j][x][y]=inf;
        dp[1][1][1][0]=0,dp[1][1][0][0]=a[1][1];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                for(int x=0;x<=min(i+j-1,k);x++)
                    for(int y=0,v;y<=k;y++)
                        if((v=dp[i][j][x][y])>inf)
                        {
                            if(j<m) upd(dp[i][j+1][x+1][y],v),upd(dp[i][j+1][x][y],v+a[i][j+1]);
                            if(i<n) for(int t=0;t<=min(m-1,k-y);t++)
                                upd(dp[i+1][j][x+1][y+t],v+ps[i+1][j][t]),
                                upd(dp[i+1][j][x][y+t],v+ps[i+1][j][t]+a[i+1][j]);
                        }
        int ans=inf;
        for(int i=0;i<=k;i++) upd(ans,dp[n][m][i][i]);
        printf("%d\n",ans);
    }
    return 0;
}
D
题目大意

给一颗点带权的树,多次询问树上路径出现偶数次(0次也算)的最小的正整数是什么。多组数据。 n , a , q 2 e 5

题解

首先显然可以莫队/分块,但多组数据T的飞起且没有优化余地
考虑一个看上去很暴力的做法,对每个点维护一个bitset,记录哪些数字出现偶数次,询问的时候就把对应的bitset异或一下然后特殊的再异或上一个1<< a[LCA],然后找最早出现的0即可。考虑优化这个过程,每个bitset都可以用它的fa修改一条链得到,询问的时候先把其中一条链的LCA的权值扣掉,然后去和另一个搞,我们发现这个过程实在线段树上二分的过程:每次判断两个左子区间是否异或起来全是1,是就往右走,不是就往左走;而这个判断过程显然可以通过对每个区间维护这个bitset的哈希值以及bitset xor 1111…111的哈希值,这样对应判断两个哈希值是否相等即可。大约极限数据要开7e6的线段树空间。(吐槽12s的题跑了11.2s是种怎样的体验……)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<assert.h>
#define gc getchar()
#define lint long long
#define BAS 37
#define LOG 22
#define S 10000010
#define N 200010
#define uint unsigned long long
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int ch[S][2],scnt,a[N],mi[N],T[N];
uint hv[S],hv2[S];
int build(int &x,int l,int r)
{
    x=++scnt,ch[x][0]=ch[x][1]=0;
    if(l==r) return hv[x]=0,hv2[x]=1,0;int mid=(l+r)>>1;
    build(ch[x][0],l,mid),build(ch[x][1],mid+1,r);
    return hv[x]=hv[ch[x][0]]+mi[mid-l+1]*hv[ch[x][1]],
        hv2[x]=hv2[ch[x][0]]+mi[mid-l+1]*hv2[ch[x][1]],0;
}
int update(int &x,int y,int l,int r,int p)
{
    x=++scnt,ch[x][0]=ch[y][0],ch[x][1]=ch[y][1];
    if(l==r) return hv[x]=hv2[y],hv2[x]=hv[y],0;int mid=(l+r)>>1;
    if(p<=mid) update(ch[x][0],ch[y][0],l,mid,p);
    else update(ch[x][1],ch[y][1],mid+1,r,p);
    return hv[x]=hv[ch[x][0]]+mi[mid-l+1]*hv[ch[x][1]],
        hv2[x]=hv2[ch[x][0]]+mi[mid-l+1]*hv2[ch[x][1]],0;
}
int query(int x,int y,int l,int r)
{
    if(l==r) return l;int mid=(l+r)>>1;
    if(hv[ch[x][0]]^hv2[ch[y][0]])
        return query(ch[x][0],ch[y][0],l,mid);
    return query(ch[x][1],ch[y][1],mid+1,r);
}
struct edges{
    int to,pre;
}e[N<<1];int h[N],etop,L,d[N],up[N][LOG],Log[N];
inline int add_edge(int u,int v)
{   return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop;   }
int dfs(int x,int fa)
{
    update(T[x],T[fa],1,L,a[x]);
    memset(up[x],0,sizeof(up[x]));
    up[x][0]=fa,d[x]=d[fa]+1;
    for(int i=1;i<=Log[d[x]];i++)
        up[x][i]=up[up[x][i-1]][i-1];
    for(int i=h[x],y;i;i=e[i].pre)
        if((y=e[i].to)^fa) dfs(y,x);
    return 0;
}
inline int getLCA(int x,int y)
{
    if(d[x]<d[y]) swap(x,y);
    for(int i=Log[d[x]];i>=0;i--)
        if(d[up[x][i]]>=d[y]) x=up[x][i];
    if(x==y) return x;
    for(int i=Log[d[x]];i>=0;i--)
        if(up[x][i]^up[y][i]) x=up[x][i],y=up[y][i];
    return up[x][0];
}
int show(int x,int l=1,int r=L)
{
    debug(l)sp,debug(r)sp,debug(hv[x])sp,debug(hv2[x])ln;//,debug(ch[x][0])sp,debug(ch[x][1])ln;
    int mid=(l+r)>>1;if(l==r) return 0;
    return show(ch[x][0],l,mid),show(ch[x][1],mid+1,r);
}
int main()
{
//  freopen("a.in","r",stdin);
//  freopen("std.out","w",stdout);
    for(int i=mi[0]=1;i<=200000;i++) mi[i]=mi[i-1]*BAS;
    for(int i=2;i<=200000;i++) Log[i]=Log[i>>1]+1;
    for(int Tc=inn();Tc;Tc--)
    {
        int n=inn(),m=inn(),maxa=0,tmp;L=0;
        for(int i=1;i<=n;i++) maxa=max(maxa,a[i]=inn());
        memset(h,0,sizeof(int)*(n+1)),etop=0;
        for(int i=1,u,v;i<n;i++)
            u=inn(),v=inn(),add_edge(u,v),add_edge(v,u);
        scnt=0,build(T[0],1,L=maxa+1),dfs(1,0);
//      for(int i=1;i<=n;i++) debug(i)ln,show(T[i]),cerr ln;
        while(m--)
        {
            int x=inn(),y=inn(),c=getLCA(x,y);
            update(tmp,T[x],1,L,a[c]);
            printf("%d\n",query(tmp,T[y],1,L));
        }
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/mys_c_k/article/details/80471409