noip模拟72[阴间]

noip模拟72 solutions

怎么说我今天觉得这个题特别难??但是别人几乎\(AK\)??

咱也不知道为啥,自己感觉非常难,但是改题确实挺通畅的

所以以后要把所有题都看得简单一些,这样就可以有更多的动力去做题

总是想不到正解??因为见到的题太少了。。。。

T1 出了个大阴间题

这题对我来说确实阴间!!!

至于为什么,我之前见到数据范围不够\(dfs\)的就想状压,然后正解就是\(meet in the middle\)

然后我就充分发挥老祖宗的箴言---吃一堑长一智!!!

这次想都不带想状压,直接\(meet in the middle\)

然后就直接导致我特别想睡觉,就睡过去了,就直接打完暴力就走了。。

按说状压确实挺简单的,这第一维就是选的数的状态,第二维就是当前的\(a\)

但是你发现吧,这个数组我开不出来啊,其实用\(map\)也是可以过得

仔细观察,\(a\)要么是所有选的数的最大值,或者是最大值加一

这样的话最后一维就只剩下两个了,转移就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=25;
const int mod=1e9+7;
int n,k,tag,ans;
struct node{int a,id;}sca[N];
bool cmp1(node x,node y){return x.a==y.a?x.id<y.id:x.a<y.a;}
bool cmp2(node x,node y){return x.id<y.id;}
int f[1<<18][N],g[1<<18][N],mx[1<<18],num[1<<18];
signed main(){
    #ifdef oj
        freopen("repair.in","r",stdin);
        freopen("repair.out","w",stdout);
    #endif
    scanf("%lld%lld",&n,&k);
    fo(i,1,n)scanf("%lld",&sca[i].a);
    int U=(1<<n)-1;
    fo(s,1,U){
        int tot=0,a;
        fo(i,1,n){
            if((s>>i-1)&1){
                mx[s]=max(mx[s],sca[i].a);
                tot++;
            }
        }
        num[s]=tot;
        if(tot==1)g[s][0]=1;
    }
    fo(s,1,U){
        fo(j,0,1){
            if(g[s][j]==0)continue;
            fo(i,1,n){
                if((s>>i-1)&1)continue;
                int now=j+mx[s];
                int tmp=now==sca[i].a?now+1:max(now,sca[i].a);
                (f[s|(1<<i-1)][tmp-mx[s|(1<<i-1)]]+=(f[s][j]%mod+(k*tmp%mod+(1ll<<num[s]-1)-1)%mod*(g[s][j]%mod))%mod)%=mod;
                g[s|(1<<i-1)][tmp-mx[s|(1<<i-1)]]+=g[s][j];
            }
        }
    }
    fu(i,1,0)if(g[U][i]){printf("%lld %lld",mx[U]+i,f[U][i]);return 0;}
}

T2 最简单辣快来做

这个我考场上只想到了一半,这个题的处理方式和天使玩偶挺像的

你要拆开绝对值,所以可以将整张图处理四遍,分别对应四种拆绝对值的办法

但是题目很***它的模数不是质数,我不能求逆元,没有拿到满分

我上面思路的实现办法是树状数组,二维的,其实直接求前缀和好像也是可以的

其实在考场上我实现这个东西的时候很没有把握,最后直接硬生生测试点分治给加上了

没想到最后测了一下大样例竟然过了!考场上要相信自己

82pts
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=2005;
const int M=2e5+5;
int n,Q,ln,lm,mod,da,db;
struct node{int h,x,y;}sca[N],qus[M];
int lsh[N*2],lh;
void exgcd(int a,int b,int &x,int &y){
    if(b==0)return x=1,y=0,void();
    exgcd(b,a%b,x,y);
    int tmp=x;
    x=y;y=tmp-a/b*y;
    return ;
}
int inv(int a){
    int x,y;
    exgcd(a,mod,x,y);
    if(x<0)x+=(-x)/mod*mod+mod;
    if(x>=mod)x-=x/mod*mod;
    return x;
}
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
struct BIT{
    int tr[N*2][N*2];
    void add(int x,int y,int v){
        for(int i=x;i<=lh;i+=(i&(-i)))
            for(int j=y;j<=lh;j+=(j&(-j)))
                tr[i][j]=(tr[i][j]+v)%mod;
        return ;
    }
    int query(int x,int y){
        if(x==0||y==0)return 0;
        int ret=0;
        for(int i=x;i;i-=(i&(-i)))
            for(int j=y;j;j-=(j&(-j)))
                ret=(ret+tr[i][j])%mod;
        return ret;
    }
    int ask(int x1,int y1,int x2,int y2){//x1<x2 y1<y2
        return (query(x2,y2)+query(x1,y1)+2*mod-query(x1,y2)-query(x2,y1))%mod;
    }
}t1,t2,t3,t4;
void spj(){
    fo(i,1,n){
        scanf("%lld%lld%lld",&sca[i].h,&sca[i].x,&sca[i].y);
        lsh[++lh]=sca[i].x;lsh[++lh]=sca[i].y;
    }
    sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;
    fo(i,1,n){
        int x=lower_bound(lsh+1,lsh+lh+1,sca[i].x)-lsh;
        int y=lower_bound(lsh+1,lsh+lh+1,sca[i].y)-lsh,h=sca[i].h;
        int kx=ksm(da,lsh[x]),ky=ksm(db,lsh[y]);
        t1.add(x,y,h*inv(kx)%mod*inv(ky)%mod);
        t2.add(x,y,h*inv(kx)%mod*ky%mod);
        t3.add(x,y,h*kx%mod*inv(ky)%mod);
        t4.add(x,y,h*kx%mod*ky%mod);
    }
    while(Q--){
        int p,q,ans=0;scanf("%lld%lld",&p,&q);
        int ip=upper_bound(lsh+1,lsh+lh+1,p)-lsh-1;
        int iq=upper_bound(lsh+1,lsh+lh+1,q)-lsh-1;
        //cout<<ip<<" "<<iq<<endl;
        int kp=ksm(da,p),kq=ksm(db,q);
        ans=(ans+t1.ask(0,0,ip,iq)*kp%mod*kq%mod)%mod;//cout<<t1.ask(0,0,ip,iq)<<" "<<ans<<endl;
        ans=(ans+t2.ask(0,iq,ip,lh)*kp%mod*inv(kq)%mod)%mod;//cout<<ans<<endl;
        ans=(ans+t3.ask(ip,0,lh,iq)*inv(kp)%mod*kq%mod)%mod;//cout<<ans<<endl;
        ans=(ans+t4.ask(ip,iq,lh,lh)*inv(kp)%mod*inv(kq)%mod)%mod;//cout<<ans<<endl;
        printf("%lld\n",ans);
    }
}
signed main(){
    #ifdef oj
        freopen("satellite.in","r",stdin);
        freopen("satellite.out","w",stdout);
    #endif
    scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&Q,&ln,&lm,&mod,&da,&db);
    if(n*Q>500000){spj();return 0;}
    fo(i,1,n)scanf("%lld%lld%lld",&sca[i].h,&sca[i].x,&sca[i].y);
    while(Q--){
        int p,q,ans=0;scanf("%lld%lld",&p,&q);
        fo(i,1,n)ans=(ans+sca[i].h*ksm(da,abs(p-sca[i].x))%mod*ksm(db,abs(q-sca[i].y))%mod)%mod;
        printf("%lld\n",ans);
    }
}

正解就是二维前缀和,离散化之后只剩下\(n^2\)个点,所有纵坐标和横坐标的交点

在离散化数组中查询你询问的坐标,你会发现它不在格点上

我们仍然在四个方向上做二维前缀和,这时候每一次转移都要乘上转移时的系数也就是\(a,b\)

最后我们直接乘上询问点到它所在的格子的四个格点的贡献就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=2005;
const int M=2e5+5;
int n,Q,ln,lm,mod,da,db;
struct node{int h,x,y;}sca[N];
int lnsh[N],lnh,lmsh[N],lmh;
int jz[4][N][N],dn[N],dm[N];
int ksm(int x,int y){
    if(y<0)return 0;
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
signed main(){
    #ifdef oj
        freopen("satellite.in","r",stdin);
        freopen("satellite.out","w",stdout);
    #endif
    scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&Q,&ln,&lm,&mod,&da,&db);
    fo(i,1,n){
        scanf("%lld%lld%lld",&sca[i].h,&sca[i].x,&sca[i].y);
        lnsh[++lnh]=sca[i].x;lmsh[++lmh]=sca[i].y;
    }
    sort(lnsh+1,lnsh+lnh+1);lnh=unique(lnsh+1,lnsh+lnh+1)-lnsh-1;
    sort(lmsh+1,lmsh+lmh+1);lmh=unique(lmsh+1,lmsh+lmh+1)-lmsh-1;
    fo(i,1,n){
        int x=lower_bound(lnsh+1,lnsh+lnh+1,sca[i].x)-lnsh;
        int y=lower_bound(lmsh+1,lmsh+lmh+1,sca[i].y)-lmsh;
        jz[0][x][y]=(jz[0][x][y]+sca[i].h)%mod;
        jz[1][x][y]=(jz[1][x][y]+sca[i].h)%mod;
        jz[2][x][y]=(jz[2][x][y]+sca[i].h)%mod;
        jz[3][x][y]=(jz[3][x][y]+sca[i].h)%mod;
    }
    fo(i,1,lnh)dn[i]=ksm(da,lnsh[i]-lnsh[i-1]);
    fo(i,1,lmh)dm[i]=ksm(db,lmsh[i]-lmsh[i-1]);
    fo(i,1,lnh)fo(j,1,lmh)jz[0][i][j]=(jz[0][i][j]+jz[0][i-1][j]*dn[i]%mod+jz[0][i][j-1]*dm[j]%mod+mod-jz[0][i-1][j-1]*dn[i]%mod*dm[j]%mod)%mod;
    fo(i,1,lnh)fu(j,lmh,1)jz[1][i][j]=(jz[1][i][j]+jz[1][i-1][j]*dn[i]%mod+jz[1][i][j+1]*dm[j+1]%mod+mod-jz[1][i-1][j+1]*dn[i]%mod*dm[j+1]%mod)%mod;
    fu(i,lnh,1)fo(j,1,lmh)jz[2][i][j]=(jz[2][i][j]+jz[2][i+1][j]*dn[i+1]%mod+jz[2][i][j-1]*dm[j]%mod+mod-jz[2][i+1][j-1]*dn[i+1]%mod*dm[j]%mod)%mod;
    fu(i,lnh,1)fu(j,lmh,1)jz[3][i][j]=(jz[3][i][j]+jz[3][i+1][j]*dn[i+1]%mod+jz[3][i][j+1]*dm[j+1]%mod+mod-jz[3][i+1][j+1]*dn[i+1]%mod*dm[j+1]%mod)%mod;
    while(Q--){
        int x,y,ans=0;scanf("%lld%lld",&x,&y);
        int px=upper_bound(lnsh+1,lnsh+lnh+1,x)-lnsh-1;
        int py=upper_bound(lmsh+1,lmsh+lmh+1,y)-lmsh-1;
        ans=(ans+jz[0][px][py]*ksm(da,x-lnsh[px])%mod*ksm(db,y-lmsh[py])%mod)%mod;
        ans=(ans+jz[1][px][py+1]*ksm(da,x-lnsh[px])%mod*ksm(db,lmsh[py+1]-y)%mod)%mod;
        ans=(ans+jz[2][px+1][py]*ksm(da,lnsh[px+1]-x)%mod*ksm(db,y-lmsh[py])%mod)%mod;
        ans=(ans+jz[3][px+1][py+1]*ksm(da,lnsh[px+1]-x)%mod*ksm(db,lmsh[py+1]-y)%mod)%mod;
        printf("%lld\n",ans);
    }
}

T3 是我的你不要抢

对不起,我这个题并不是正解,但是记忆化的\(hash\)使得复杂度有了保障??

最大好像是根号??

不知道不知道,粘上码就溜走

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define ull unsigned long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=6e5+5;
const ull bas=131;
char a[N];
vector<ull> hs[N];
int n,Q,len[N],mx;
int s,t;
ull ba[N];
ull get(int i,int l,int r){return hs[i][r]-hs[i][l-1]*ba[r-l+1];}
map<pair<int,int>,int> mp;
signed main(){
    #ifdef oj
        freopen("string.in","r",stdin);
        freopen("string.out","w",stdout);
    #endif
    scanf("%d%d",&n,&Q);
    fo(i,1,n){
        scanf("%s",a+1);
        len[i]=strlen(a+1);
        mx=max(mx,len[i]);
        hs[i].resize(len[i]+5);
        fo(j,1,len[i])hs[i][j]=hs[i][j-1]*bas+a[j];
    }
    ba[0]=1;fo(i,1,mx)ba[i]=ba[i-1]*bas;
    while(Q--){
        int x,y,ans=0;scanf("%d%d",&x,&y);
        if(mp.find(make_pair(x,y))!=mp.end()){
            printf("%d\n",mp[make_pair(x,y)]);
            continue;
        }
        fu(i,min(len[x],len[y]),1){
            if(get(x,len[x]-i+1,len[x])==get(y,1,i)){ans=i;break;}
        }
        printf("%d\n",ans);
        mp.insert(make_pair(make_pair(x,y),ans));
    }
}

T4 显然也是我整的

显然我不会做。。。。。。

怎么说这个题是个找规律的题?

官方题解的构造我没看懂就\(A\)掉了?

无法看懂,只能口胡

首先对于长度小于\(\frac{n}{2}\)的,对于这些求\(GCD\)可以得到他们可以划分出来的块

大于的话,中间会剩下好多啊,删掉就好了,不影响答案

可能会有一些长度加上\(GCD\)之后就大于\(n\)了,这个也可以求\(GCD\)

然后你吧把求过\(GCD\)的都删掉,直接向下递归就行了

为甚可以向下递归,因为你前面所有的可以按照\(GCD\)分组,只需要留下一个然后加上余数就好了

别忘了向下递归之前把刚刚求出来的\(GCD\)放到你的序列里去

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=2e5+5;
int T,n,m;
set<int> st;
int sta[N],cnt;
int sol(int n){
    int ret=0,gcd=0;
    if(*st.begin()>n/2){
        int B=2*(*st.begin())-n;
        ret+=B;n-=B;cnt=0;
        for(int i:st)sta[++cnt]=i;
        st.clear();fo(i,1,cnt)st.insert(sta[i]-B);
    }
    while(st.begin()!=st.end()&&*st.begin()<=n/2){
        gcd=__gcd(gcd,*st.begin());
        st.erase(st.begin());
    }
    set<int>::iterator it=st.begin();
    while(it!=st.end()&&*it+gcd<=n)gcd=__gcd(gcd,*it),it=next(it);
    while(st.begin()!=st.end()&&*st.begin()+gcd<=n)st.erase(st.begin());
    if(st.begin()==st.end())return ret+gcd;
    int newn=gcd+n%gcd;cnt=0;
    for(int i:st)sta[++cnt]=i;st.clear();
    fo(i,1,cnt)st.insert(sta[i]+newn-n);st.insert(gcd);
    return ret+sol(newn);
}
signed main(){
    #ifdef oj
        freopen("graph.in","r",stdin);
        freopen("graph.out","w",stdout);
    #endif
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&m);
        fo(i,1,m){
            int x;scanf("%lld",&x);
            st.insert(x);
        }
        printf("%lld\n",sol(n));
    }
}
posted @ 2021-10-09 19:21  fengwu2005  阅读(107)  评论(0)    收藏  举报