1. 程式人生 > >SNOI2017(BZOJ5015~5018)泛做

SNOI2017(BZOJ5015~5018)泛做

emp 數據 return turn const cfb \n noi eve

T1:禮物

想錯方向了,實際上很簡單。

我想的是:顯然題目求的是$\sum_{i=1}^{n} i^{k}2^{i}$,然後或許可以通過化式子變成與n無關的復雜度?

然後就不停往斯特林數反演和下降冪的方向想,最後什麽都沒想出來。

其實想一會就應該意識到:這完全就是一個不可直接化簡的式子啊。

$A[i]$表示第$i$個人送的禮物數,$S[i]$為前綴和,那麽顯然有$S[i]=2*S[i-1]+i^k$,這樣就把思路引導矩乘方面去了。

如何矩乘呢?考慮題目的提示。首先$i^k$這個東西不好直接轉移到$(i+1)^k$,其次發現轉移的話根據二項式定理需要$i^1,i^2,...,i^k$的所有數,最後從題目“$K \leq 10$”可以猜到,一定是將$i^1 i^2 ... i^k$全部扔進矩陣,和$S[i]$一起轉移,轉移矩陣則正好是$C_i^j$的轉置矩陣,這樣就可以$O(\log n \times k^3)$解決問題

技術分享圖片
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 typedef long long ll;
 6 using namespace std;
 7 
 8 const ll P=1000000007;
 9 ll n,c[20][20];
10 int m;
11 
12 struct M{ ll a[20][20]; M(){ memset(a,0,sizeof(a)); } }tr,a1,a2;
13 M mul(M a,M b){ 14 M c; 15 rep(i,0,m+1) rep(j,0,m+1) rep(k,0,m+1) c.a[i][k]=(c.a[i][k]+a.a[i][j]*b.a[j][k])%P; 16 return c; 17 } 18 19 M ksm(M a,ll b){ 20 M x=tr; 21 for (; b; x=mul(x,x),b>>=1) 22 if (b & 1) a=mul(a,x); 23 return a; 24 } 25 26 int
main(){ 27 scanf("%lld%d",&n,&m); 28 rep(i,0,m) c[i][0]=1; 29 rep(i,1,m) rep(j,1,i) c[i][j]=(c[i-1][j-1]+c[i-1][j])%P; 30 rep(i,0,m){ 31 a1.a[0][i]=a2.a[0][i]=1; 32 rep(j,0,i) tr.a[j][i]=c[i][j]; 33 } 34 tr.a[m+1][m+1]=2; tr.a[m][m+1]=1; 35 a1=ksm(a1,n-1); a2=ksm(a2,n); 36 printf("%lld\n",(a2.a[0][m+1]-a1.a[0][m+1]+P)%P); 37 return 0; 38 }
View Code

T2:一個簡單的詢問

求和上面的無窮符號第一眼有點嚇人,實際上就是n。

看數據範圍就知道是分塊。怎麽分?這個雖然簡單,但還是有點難想的。$$get(l1,r1,x)\times get(l2,r2,x)$$$$= (get(l1,r1,x)-get(1,l1-1,x)) \times (get(l2,r2,x)-get(1,l2-1,x))$$$$= get(1,r1,x)\times get(1,r2,x)-get(1,r1,x) \times get(1,l2-1,x)-get(1,l1-1,x) \times get(1,r2,x)+get(1,l1-1,x) \times get(1,l2-1,x)$$

這樣就可以看出來是莫隊了,這裏的莫隊已經不是傳統的$[L,R]$區間詢問了,而是真正的曼哈頓最小生成樹的替代品。

技術分享圖片
 1 #include<cmath>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 typedef long long ll;
 6 using namespace std;
 7 
 8 const int N=50100;
 9 ll res[N],ans;
10 int n,m,a[N],l1,r1,l2,r2,L,R,tot,num1[N],num2[N];
11 struct Q{ int l,r,k,id,pos; }q[N<<2];
12 bool cmp(Q a,Q b){ return (a.pos==b.pos) ? a.r<b.r : a.pos<b.pos; }
13 
14 int main(){
15     scanf("%d",&n); int bl=sqrt(n);
16     rep(i,1,n) scanf("%d",&a[i]);
17     scanf("%d",&m);
18     rep(i,1,m){
19         scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
20         q[++tot]=(Q){l1-1,l2-1,1,i,(l1-2)/bl+1}; q[++tot]=(Q){r1,r2,1,i,(r1-1)/bl+1};
21         q[++tot]=(Q){l1-1,r2,-1,i,(l1-2)/bl+1}; q[++tot]=(Q){r1,l2-1,-1,i,(r1-1)/bl+1};
22     }
23     sort(q+1,q+tot+1,cmp);
24     rep(i,1,tot){
25         while (R<q[i].r) ans+=num1[a[++R]],num2[a[R]]++;
26         while (L>q[i].l) ans-=num2[a[L]],num1[a[L--]]--;
27         while (R>q[i].r) ans-=num1[a[R]],num2[a[R--]]--;
28         while (L<q[i].l) ans+=num2[a[++L]],num1[a[L]]++;
29         res[q[i].id]+=ans*q[i].k;
30     }
31     rep(i,1,m) printf("%lld\n",res[i]);
32     return 0;
33 }
View Code

T3:炸彈

想不到想不到,線段樹的新應用。
首先可以看出是圖論,每個炸彈向可以引爆的炸彈連邊,強連通塊$Tarjan$縮點後建出反圖,再按拓撲序跑一遍$DP$計數即可,這是暴力。
發現每個點連出去的所有點一定是一段連續的區間,所以可以想到線段樹。所以我們先按照線段樹的方法建點連邊,這樣最後最多只會有$O(n\log n)$個點和$O(n\log n)$條邊了。
發現有不少關於區間的問題可以往線段樹方面想。
無故$CE$了$5$發,至今未$AC$,改了$cin$和$cout$也過不了,明明沒有任何編譯錯誤信息,辣雞$BZOJ$。

技術分享圖片
 1 #include<cstdio>
 2 #include<queue>
 3 #include<iostream>
 4 #include<algorithm>
 5 #define ls (x<<1)
 6 #define rs ((x<<1)|1)
 7 #define lson ls,L,mid
 8 #define rson rs,mid+1,R
 9 #define rep(i,l,r) for (int i=l; i<=r; i++)
10 typedef long long ll;
11 using namespace std;
12 
13 const int N=2000100,M=20000100,mod=1000000007;
14 int scc,n,tim,top,dfn[N],low[N],ind[N],inq[N],stk[N],bel[N],pos[N];
15 ll ans,a[N],p[N],v[N],Min[N],Max[N],mn[N],mx[N];
16 queue<int>Q;
17 
18 struct Graph{
19     int cnt,to[M],nxt[M],h[N];
20     void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
21     void tarjan(int x){
22         dfn[x]=low[x]=++tim; inq[x]=1; stk[++top]=x;
23         for (int i=h[x],k; i; i=nxt[i])
24             if (!dfn[k=to[i]]) tarjan(k),low[x]=min(low[x],low[k]);
25                 else if (inq[k]) low[x]=min(low[x],dfn[k]);
26         if (low[x]==dfn[x]){
27             scc++; int t;
28             do{
29                 t=stk[top--]; bel[t]=scc; inq[t]=0;
30                 Min[scc]=min(Min[scc],mn[t]);
31                 Max[scc]=max(Max[scc],mx[t]);
32             }while (t!=x);
33         }
34     }
35 }G,G1;
36 
37 void build(int x,int L,int R){
38     if (L==R) { pos[L]=x; return; }
39     int mid=(L+R)>>1; mn[x]=1ll<<62; mx[x]=-(1ll<<62);
40     build(lson); build(rson);
41     G.add(x,ls); G.add(x,rs);
42 }
43 
44 void upd(int x,int L,int R,int k,int l,int r){
45     if (L==l && r==R) { G.add(k,x); return; }
46     int mid=(L+R)>>1;
47     if (r<=mid) upd(lson,k,l,r);
48     else if (l>mid) upd(rson,k,l,r);
49         else upd(lson,k,l,mid),upd(rson,k,mid+1,r);
50 }
51 
52 int main(){
53     freopen("bzoj5017.in","r",stdin);
54     freopen("bzoj5017.out","w",stdout);
55     ios::sync_with_stdio(false);
56     cin>>n; build(1,1,n);
57     rep(i,1,n) cin>>a[i]>>v[i],mn[pos[i]]=mx[pos[i]]=a[i];
58     a[n+1]=1ll<<62;
59     rep(i,1,n){
60         int l=lower_bound(a+1,a+n+2,a[i]-v[i])-a;
61         int r=upper_bound(a+1,a+n+2,a[i]+v[i])-a-1;
62         upd(1,1,n,pos[i],l,r);
63     }
64     rep(i,1,n<<2) if (!dfn[i]) G.tarjan(i);
65     rep(x,1,n<<2) for (int i=G.h[x],k; i; i=G.nxt[i])
66         if (bel[x]!=bel[k=G.to[i]]) G1.add(bel[k],bel[x]),ind[bel[x]]++;
67     rep(i,1,scc) if (!ind[i]) Q.push(i);
68     while (!Q.empty()){
69         int x=Q.front(); Q.pop();
70         for (int i=G1.h[x],k; i; i=G1.nxt[i]){
71             ind[k=G1.to[i]]--; Min[k]=min(Min[k],Min[x]),Max[k]=max(Max[k],Max[x]);
72             if (!ind[k]) Q.push(k);
73         }
74     }
75     rep(i,1,n){
76         int r=upper_bound(a+1,a+n+1,Max[bel[pos[i]]])-a;
77         int l=lower_bound(a+1,a+n+1,Min[bel[pos[i]]])-a;
78         ans=(ans+1ll*(r-l)*i)%mod;
79     }
80     cout<<ans<<endl;
81     return 0;
82 }
View Code

T4:英雄聯盟。
一眼想出暴力做法,發現連暴力分都拿不到,然後就不會了。
一看題解,發現暴力就是正解,十分崩潰。
$f[i][j]$表示前$i$個英雄花$j$元所能得到的最多方案數,直接轉移,普及組難度。
可能會爆$long long$,所以所有大於$m$的都記成等於就好了。

技術分享圖片
 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=l; i<=r; i++)
 4 typedef long long ll;
 5 using namespace std;
 6 
 7 ll f[125][250010],m,t;
 8 int n,c[125],k[125];
 9 
10 int main(){
11     scanf("%d%lld",&n,&m);
12     rep(i,1,n) scanf("%d",&k[i]);
13     rep(i,1,n) scanf("%d",&c[i]);
14     f[0][0]=1;
15     rep(i,1,n){
16         rep(j,0,t) f[i][j]=f[i-1][j];
17         rep(j,2,k[i]) rep(l,c[i]*j,t+c[i]*j)
18             f[i][l]=min(m,max(f[i][l],(ll)f[i-1][l-j*c[i]]*j));
19         t+=c[i]*k[i];
20     } 
21     rep(i,0,t) if(f[n][i]>=m) { printf("%d\n",i); return 0; }
22     return 0;
23 }
View Code

SNOI2017(BZOJ5015~5018)泛做