1. 程式人生 > >[JZOJ NOIP2018模擬10.19]

[JZOJ NOIP2018模擬10.19]

T1寫炸了今天,期望70卻落了個20...連鏈上的都沒有寫對

T3什麼什麼線段樹分治套AC自動機,表示我完全自閉了,幸好考場上沒有槓T3

總體比賽還是比較舒服,暴力分給的蠻足的,不像昨天那樣


 

T1:

題目連結:

http://172.16.0.132/senior/#main/show/5913

題目:

裡口福因有林下風氣,帶領全國各地高校掀起了一股AK風,大家都十分痴迷於AK。裡口福為了打擊大家的自信心,出了一道自以為十分困難的題目。
裡口福有一棵樹,第i個節點上有點權ai,他的問題就是這棵樹中有多少個不同的連通塊滿足連通塊的最大值與最小值之差=k,兩個連通塊不同當且僅當至少存在一個節點在一個連通塊中出現而另一個連通塊中沒有出現。


痴迷於AK的你馬上接下這道題目,在裡口福狂妄的笑聲中,你切掉這道題的決心更加堅定了,現在就差你的程式碼了。

題解:

我們列舉一下最小值,這樣最大值就是唯一確定的了

以最小值為根跑$dp$,$dp(x,r,k)$表示最小值是$a[r]$,當前在$x$節點,最大值和最小值不超過$k$的連通塊的個數

當$a[r]<=a[y]<=a[r]+k$時可以轉移

發現這樣以兩個a值相等的點為根可能方案會算重,所以當$a[r]==a[y]$時我們強制讓y<r的時候才可以轉移

顯然若當前列舉的最小值為a[i],對答案的貢獻就是$dp(i,i,k)-dp(i,i,k-1)$

#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
using namespace std;

const int N=3333+15;
const int mo=19260817;
int n,k,tot;
int a[N],head[N];
struct E{
    int to,nxt;
}edge[N<<1];
inline int read()
{
    char ch=getchar();
    
int s=0,f=1; while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } void link(int u,int v){edge[++tot]=(E){v,head[u]};head[u]=tot;} int dp(int x,int pre,int r,int k) { if (k<0) return 0; int re=1; for (int i=head[x];i;i=edge[i].nxt) { int y=edge[i].to; if (y==pre) continue; if (a[y]>=a[r]&&a[y]<=a[r]+k&&(a[y]!=a[r]||(a[y]==a[r]&&y<r))) { re=1ll*re*(dp(y,x,r,k)+1)%mo; } } return re; } int main() { freopen("lkf.in","r",stdin); freopen("lkf.out","w",stdout); n=read();k=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1,u,v;i<n;i++){ u=read();v=read(); link(u,v);link(v,u); } int ans=0; for (int i=1;i<=n;i++) { ans=(ans+((dp(i,0,i,k)-dp(i,0,i,k-1))%mo+mo)%mo)%mo; } printf("%d\n",ans); return 0; }
View Code

T2: