1. 程式人生 > >【HHHOJ】NOIP2018 模擬賽(二十五) 解題報告

【HHHOJ】NOIP2018 模擬賽(二十五) 解題報告

點此進入比賽

得分: \(100+100+20=220\)\(T1\)打了兩個小時,以至於\(T3\)沒時間打了,無奈交暴力)

排名: \(Rank\ 8\)

\(Rating\)\(+19\)


\(T1\):【HHHOJ126】求和(點此看題面

看到這道題,我不由得想到這道題目:【BZOJ1257】[CQOI2007] 餘數之和

於是就想到用除法分塊去做。

但是,由於記錯了平方和公式(我畢竟還是太弱了... ...),結果打了兩個小時... ...

如果我們暫時把\(i≠j\)這個詭異的限制拋開不顧,則不難發現,\((n\%i)\)這一項與\(j\)無關,\((m\%j)\)這一項與\(i\)

無關,所以我們可以把原式分成兩部分:

\[\sum_{i=1}^n(n\%i)*\sum_{j=1}^m(m\%j)\]

如果用\(f(x)\)表示\(\sum_{i=1}^x(x\%i)\),則原式就可以表示為:

\[f(n)·f(m)\]

關於\(f(n)\)怎麼求,我想在【BZOJ1257】[CQOI2007] 餘數之和這題的題解中我應該講得很清楚了吧。

但是,此時求得的答案包括了\(i=j\)的情況。

所以我們要將\(i=j\)的答案從答案中減去。

首先,不難發現,在\(i=j\)時的答案是長這樣的(代入原式計算即可):

\[\sum_{i=1}^{min(n,m)}(n\%i)(m\%i)\]

依據對取模運算轉化的常見套路,我們可以將其轉化成這個樣子:

\[\sum_{i=1}^{min(n,m)}(n-\lfloor\frac ni\rfloor*i)(m-\lfloor\frac mi\rfloor*i)\]

拆括號得:

\[\sum_{i=1}^{min(n,m)}(nm-mi\lfloor\frac ni\rfloor-ni\lfloor\frac mi\rfloor+i^2\lfloor\frac ni\rfloor\lfloor\frac mi\rfloor)\]

這個式子顯然是可以用除法分塊來做的。

於是就解決了呀。

#include<bits/stdc++.h>
#define LL long long
#define ten(x) (((x)<<3)+((x)<<1))
#define MOD 19940417
#define min(x,y) ((x)<(y)?(x):(y))
#define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
#define Dec(x,y) ((x-=(y))<0&&(x+=MOD))
#define f(x) ((((x)*((x)+1)>>1)%MOD)*((((x)<<1)+1)%MOD)%MOD*6646806%MOD)
#define g(x,y) ((((x)+(y))*((y)-(x)+1)>>1)%MOD)
using namespace std;
LL n,m;
class FIO
{
    private:
        #define Fsize 100000
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
        #define pc(ch) (putchar(ch))
        LL Top;char ch,*A,*B,Fin[Fsize],Stack[Fsize];
    public:
        FIO() {A=B=Fin;}
        inline void read(LL &x) {x=0;while(!isdigit(ch=tc()));while(x=ten(x)+(ch&15),isdigit(ch=tc()));} 
        inline void write(LL x) {if(!x) return (void)(pc('0'));while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);}
}F;
inline LL Operate(LL x)//除法分塊,類似於餘數求和
{
    register LL l,r,res=x*x%MOD;
    for(l=1;l<=x;l=r+1) r=x/(x/l),Dec(res,g(l,r)*(x/l)%MOD);
    return res;
}
int main()
{
    register LL l,r,lim,ans;
    F.read(n),F.read(m),ans=Operate(n)*Operate(m)%MOD;
    for(l=1,Dec(ans,(lim=min(n,m))*n%MOD*m%MOD);l<=lim;l=r+1)//根據上面求出的式子,再一次除法分塊,減去多餘的答案 
    {
        r=min(n/(n/l),m/(m/l)),
        Dec(ans,(f(r)-f(l-1))%MOD*(n/l)%MOD*(m/l)%MOD),
        Inc(ans,g(l,r)*n%MOD*(m/l)%MOD),
        Inc(ans,g(l,r)*m%MOD*(n/l)%MOD);
    }
    return F.write(ans),0; 
}

\(T2\):【HHHOJ127】排隊(點此看題面

這題沒想到竟能在考試時間內做出來。

快速冪寫炸調了半個多小時祭。(深深感受到了自己的菜)

首先,這種題目一看到,肯定先敲一個組合數板子

然後就是亂搞(\(DFS\)遍歷\(+\)玄學轉移),結果莫名其妙過了樣例!

更神奇的是,一交居然\(A\)了!

其實這題也不是很難,就是組合數的一個應用,感覺還是挺好推的,這裡就省略了。

具體實現可以見程式碼:

#include<bits/stdc++.h>
#define ten(x) (((x)<<3)+((x)<<1))
#define N 1000 
#define MOD 10007
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,ee=0,lnk[N+5];
struct edge
{
    int to,nxt;
}e[N+5];
class FIO
{
    private:
        #define Fsize 100000
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
        #define pc(ch) (putchar(ch))
        int Top;char ch,*A,*B,Fin[Fsize],Stack[Fsize];
    public:
        FIO() {A=B=Fin;}
        inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=ten(x)+(ch&15),isdigit(ch=tc()));}
        inline void write(int x) {if(!x) return (void)(pc('0'));while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);}
        inline void write_char(char x) {pc(x);}
}F;
class Class_Math//組合數
{
    private:
        int Fac[N+5],Inv[N+5];
        inline int quick_pow(int x,int y,register int res=1)//快速冪寫炸調了半個多小時
        {
            for(;y;x=1LL*x*x%MOD,y>>=1) if(y&1) res=1LL*res*x%MOD;
            return res;
        }
    public:
        Class_Math()//預處理出階乘和階乘逆元
        {
            register int i;
            for(Fac[0]=1,i=1;i<=N;++i) Fac[i]=Fac[i-1]*i%MOD;
            for(Inv[N]=quick_pow(Fac[N],MOD-2),i=N-1;i>=0;--i) Inv[i]=Inv[i+1]*(i+1)%MOD;
        }
        inline int C(int x,int y) {return 1LL*Fac[x]*Inv[y]%MOD*Inv[x-y]%MOD;}//求組合數
}Math;
class Class_DfsSolver//DFS遍歷
{
    private:
        int ans[N+5],Size[N+5];//ans記錄當前子樹內的方案數,Size記錄子樹大小
        inline void dfs(int x)
        {
            register int i;
            for(ans[x]=1,Size[x]=0,i=lnk[x];i;i=e[i].nxt)//玄學轉移
                dfs(e[i].to),Size[x]+=Size[e[i].to],ans[x]=1LL*ans[x]*ans[e[i].to]%MOD*Math.C(Size[x]-1,Size[e[i].to]-1)%MOD;
            ++Size[x];
        }
    public:
        inline void Solve() {dfs(1),F.write(ans[1]),F.write_char('\n');}
}DfsSolver;
int main()
{
    register int i,x,y,T;F.read(T);
    while(T--)
    {
        for(F.read(n),i=1,ee=0;i<=n;++i) lnk[i]=0;
        for(i=1;i<=n;++i) for(F.read(x);x;--x) F.read(y),add(i,y);
        DfsSolver.Solve();
    }
    return 0;
}

【HHHOJ128】城市(點此看題面

原題: 【51nod1743】雪之國度

由於\(T1\)打了兩個多小時,結果雖然這題\(Manchery\)講評過,但是沒時間打了,直接交了暴力。

其實這題實現起來真的要比想象中簡單一些,題解可以參考上面的連結。

程式碼如下:

#include<bits/stdc++.h>
#define ten(x) (((x)<<3)+((x)<<1))
#define N 100000
#define M 500000
#define LogN 20
#define INF 1000000000
#define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=z)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<0?-(x):(x)) 
using namespace std;
int n,m,ee=0,val[N+5],lnk[N+5];
struct edge
{
    int from,to,nxt,val,vis;
    inline friend bool operator < (edge x,edge y) {return x.val<y.val;}
}s[M+5],e[(N<<1)+5];
class FIO
{
    private:
        #define Fsize 100000
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
        #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
        int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize];
    public:
        inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=ten(x)+(ch&15),isdigit(ch=tc()));}
        inline void write(int x) {if(!x) return (void)(pc('0'));while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);}
        inline void write_char(char x) {pc(x);}
        inline void write_string(string x) {for(register int i=0,len=x.length();i<len;++i) pc(x[i]);}
        inline void clear() {fwrite(Fout,1,FoutSize,stdout);}
}F;
class UnionFindSet
{
    public:
        int fa[N+5];
        inline void Clear() {for(register int i=1;i<=N;++i) fa[i]=i;}
        UnionFindSet() {Clear();}
        inline int getfa(int x) {return fa[x]^x?fa[x]=getfa(fa[x]):x;}
        inline void Union(int x,int y) {if((x=getfa(x))^(y=getfa(y))) fa[y]=x;}
}U;
class Class_MulSolver
{
    public:
        int Depth[N+5],fa[N+5][LogN+5],Max[N+5][LogN+5];
    private:
        inline void dfs(int x,int lst)
        {
            for(register int i=lnk[x];i;i=e[i].nxt)
                if(e[i].to^lst) Depth[e[i].to]=Depth[fa[e[i].to][0]=x]+1,Max[e[i].to][0]=e[i].val,dfs(e[i].to,x);
        }
    public:
        inline void DfsInit() {dfs(1,0);}
        inline void MulInit() {for(register int i,j=1;j<=LogN;++j) for(i=1;i<=n;++i) fa[i][j]=fa[fa[i][j-1]][j-1],Max[i][j]=max(Max[i][j-1],Max[fa[i][j-1]][j-1]);}
        inline int get_max(int x,int y)
        {
            if(Depth[x]<Depth[y]) swap(x,y);
            register int i,res=0;
            for(i=0;Depth[x]^Depth[y];++i) if((Depth[x]^Depth[y])&(1<<i)) res=max(res,Max[x][i]),x=fa[x][i];
            if(!(x^y)) return res;
            for(i=0;fa[x][i]^fa[y][i];++i);
            for(--i;i>=0;--i) if(fa[x][i]^fa[y][i]) res=max(res,max(Max[x][i],Max[y][i])),x=fa[x][i],y=fa[y][i];
            return max(res,max(Max[x][0],Max[y][0]));
        }
}MulSolver;
int main()
{
    register int i,Q,x,y;
    for(F.read(n),F.read(m),F.read(Q),i=1;i<=n;++i) F.read(val[i]);
    for(i=1;i<=m;++i) F.read(s[i].from),F.read(s[i].to),s[i].val=abs(val[s[i].from]-val[s[i].to]);
    for(sort(s+1,s+m+1),i=1;i<=m;++i) if(U.getfa(s[i].from)^U.getfa(s[i].to)) U.Union(s[i].from,s[i].to),s[i].vis=1,add(s[i].from,s[i].to,s[i].val),add(s[i].to,s[i].from,s[i].val);
    for(U.Clear(),MulSolver.DfsInit(),i=1;i<=m;++i) 
    {
        if(s[i].vis) continue;
        for(x=U.getfa(s[i].from),y=U.getfa(s[i].to);x^y;x=U.getfa(x))
        {
            if(MulSolver.Depth[x]<MulSolver.Depth[y]) swap(x,y);
            MulSolver.Max[x][0]=s[i].val,U.fa[x]=MulSolver.fa[x][0];
        }
    }
    for(MulSolver.MulInit();Q;--Q) F.read(x),F.read(y),(U.getfa(x)^U.getfa(y)?F.write_string("infinitely\n"):(F.write(MulSolver.get_max(x,y)),F.write_char('\n')));
    return F.clear(),0;
}