1. 程式人生 > 其它 >Noip模擬54 2021.9.16

Noip模擬54 2021.9.16

T1 選擇 T2 表格 T3 黑白 T4 打怪

T1 選擇

現在發現好多題目都是隱含的狀壓,不明面給到資料範圍裡,之憑藉一句話

比如這道題就是按照題目裡邊給的兒子數量不超過$10$做狀壓,非常邪門

由於資料範圍比較小,怎麼暴力就怎麼來

從葉子節點向上$dp$,狀態$i$表示每個兒子選/不選。

考慮找到那些點可以延伸到當前的$x$節點。

我們記錄一下$x$的所有直接兒子$y$,開一個$vector:son_{i,j}$記錄所有可以延伸到$i$節點的$j$

轉移的時候先看單點能否直接延伸到$x$,然後列舉任意兩個$y,yy$,看看其子樹有無兩個點$z,zz$之間有連邊

把合法的狀態記錄下來,然後列舉子集以及它補集的子集,兩兩合併來轉移,

最後看一下刪去一個子樹後答案是否會變,如果不會變,證明該子樹裡面可以有一個點直接延伸到$x$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 namespace AE86{
 4     inline int read(){
 5         int x=0,f=1;char ch=getchar();
 6         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return
x*f; 8 }inline void write(int x,char opt='\n'){ 9 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 10 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 11 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 12 }using namespace AE86; 13 14 const int
NN=1e3+5; 15 int n,m,g[NN][NN]; 16 struct SNOW{int to,next;}e[NN<<1];int head[NN],rp; 17 inline void add(int x,int y){ 18 e[++rp]=(SNOW){y,head[x]};head[x]=rp; 19 e[++rp]=(SNOW){x,head[y]};head[y]=rp; 20 } 21 vector<int> son[NN]; int Son[15],dp[1<<11],ans; 22 23 inline void dfs(int f,int x){ 24 for(int i=head[x];i;i=e[i].next) if(f!=e[i].to) dfs(x,e[i].to); 25 int num=0;for(int i=head[x];i;i=e[i].next) if(f!=e[i].to) Son[++num]=e[i].to; 26 memset(dp,0,sizeof(dp)); 27 for(int i=1;i<=num;i++) 28 for(int j=0;j<son[Son[i]].size();j++) 29 if(g[x][son[Son[i]][j]]){dp[1<<i-1]=1;break;} 30 for(int i=1;i<=num;i++){ 31 for(int j=1;j<=num;j++){ 32 if(i==j) continue; bool flag=0; 33 for(auto s1:son[Son[i]]){ 34 for(auto s2:son[Son[j]]){ 35 if(g[s1][s2]){ 36 dp[(1<<i-1)|(1<<j-1)]=1; 37 flag=1; break; 38 } 39 } 40 if(flag) break; 41 } 42 } 43 } 44 int U=(1<<num)-1,res=0; 45 for(int s=0;s<=U;s++) 46 for(int t=s;t;t=(t-1)&s) 47 dp[s]=max(dp[s],dp[t]+dp[s^t]); 48 ans+=dp[U]; 49 for(int i=1;i<=num;i++) 50 if(dp[U^(1<<i-1)]==dp[U]) 51 for(int j=0;j<son[Son[i]].size();j++) 52 son[x].push_back(son[Son[i]][j]); 53 son[x].push_back(x); 54 } 55 namespace WSN{ 56 inline short main(){ 57 freopen("select.in","r",stdin); 58 freopen("select.out","w",stdout); 59 n=read(); 60 for(int i=1,u,v;i<n;i++) 61 u=read(), v=read(), add(u,v); 62 m=read(); 63 for(int i=1,u,v;i<=m;i++) 64 u=read(), v=read(), g[u][v]=g[v][u]=1; 65 dfs(0,1); write(ans); 66 return 0; 67 } 68 } 69 signed main(){return WSN::main();}
View Code

T2 表格

雖然超綱但是是神題,比較不錯

首先發現$3*3$的表格只有六種情況,那麼問題轉化為任意選$3$行$3$列最後乘$6$

比較容易在把上下界限制轉為上界限制,即$calc(R)*6-calc(L)*6$

然後發現題目的什麼什麼距離只和卡住邊界的那兩行兩列有關,且距離為卡出來的長方形周長

不難得出一個柿子:

$\sum_{i=3}^{n}(n-i+1)(i-2)\sum_{j=3}^{min(R/2-i+1,m)}(m-j+1)(j-2)$

然後我覺得這個柿子不可能是正解(至於為什麼可能是這個柿子就吊就是$O(n)$),

於是打了一個$O(n^2)$的暴力準備對拍就去考慮$log(n)$的演算法,一直到快考完。。。

其實字首和可以優化到$O(n)$的,比較顯然,拆開後面的括號即可。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 const int mod=1e9+7;
 5 namespace AE86{
 6     inline int read(){
 7         int x=0,f=1;char ch=getchar();
 8         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 9         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
10     }inline void write(int x,char opt='\n'){
11         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
12         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
13         for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
14     inline int mo(int x){return x>=mod?x-mod:x;}
15 }using namespace AE86;
16 int n,m,L,R,ans,v6,v2;
17 inline int qmo(int a,int b){
18     int ans=1,c=mod; a%=c;
19     while(b){
20         if(b&1) ans=ans*a%c;
21         b>>=1; a=a*a%c;
22      } return ans;
23 }
24 inline int sig(int x){return (1+x)%mod*x%mod*v2%mod;}
25 inline int psi(int x){return x*(x+1)%mod*(2*x%mod+1)%mod*v6%mod;}
26 inline int calc(int x){
27     int ans=0,tmp,res,l,r,cnt;
28     for(int i=3;i<=n;++i){
29         l=3,r=min(m,x/2-i+2); if(l>r) continue;
30         tmp=(i-2)*(n-i+1)%mod;
31         res=(m+3)*(sig(r)-sig(l-1)+mod)%mod;
32         res=mo(res-(psi(r)-psi(l-1)+mod)%mod+mod);
33         res=mo(res-2*m%mod*(r-l+1)%mod-2*(r-l+1)%mod+mod);
34         ans=mo(ans+tmp*res%mod);
35     }
36     return ans;
37 }
38 namespace WSN{
39     inline short main(){
40         freopen("table.in","r",stdin);
41         freopen("table.out","w",stdout);
42         n=read(); m=read(); L=read(); R=read();
43         if(R<8) return puts("0"),0;
44         v6=qmo(6,mod-2); v2=qmo(2,mod-2);
45         if(L<=8&&(n+m-2)*2<=R){
46             int M=m*(m-1)%mod*(m-2)%mod;
47             int N=n*(n-1)%mod*(n-2)%mod;
48             write(M*N%mod*v6%mod); return 0;
49         }
50 
51         int wsn=mo(6*calc(R)%mod-6*calc(L-1)%mod+mod);
52         write(wsn);
53         return 0;
54     }
55 }
56 signed main(){return WSN::main();}
T 40

正解是拉格朗日插值,解決多項式問題,複雜度為多項式次數平方的高階演算法

詳細解釋以及板子可以參考$oi-wiki$

先把柿子化簡到這個地步:

$\sum\limits_{i=2}^{\min(n-1,x-2)}(n-i)(i-1)\sum\limits_{j=2}^{\min(x-i,m-1)}(m-j)(j-1)$

然後對於每一個多項式分別用插值計算出結果即可,複雜度為常數級別

  1 #include<bits/stdc++.h>
  2 #define int long long
  3 using namespace std;
  4 const int mod=1e9+7;
  5 namespace AE86{
  6     inline int read(){
  7         int x=0,f=1;char ch=getchar();
  8         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  9         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
 10     }inline void write(int x,char opt='\n'){
 11         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
 12         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
 13         for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
 14     inline int mo(int x){return x>=mod?x-mod:x;}
 15 }using namespace AE86;
 16 int n,m,L,R,ans,v6,v2;
 17 inline int qmo(int a,int b){
 18     int ans=1,c=mod; a%=c;
 19     while(b){
 20         if(b&1) ans=ans*a%c;
 21         b>>=1; a=a*a%c;
 22      } return ans;
 23 }
 24 inline int inv(int x){return qmo(x,mod-2);}
 25 struct node{int x,y;};node Fp[10],Gp[10],Hp[10];
 26 
 27 inline int f(int x){return (n-x)*(x-1)%mod;}
 28 inline void preF(){
 29     Fp[1]=(node){2,f(2)};
 30     Fp[2]=(node){3,mo(Fp[1].y+f(3))};
 31     Fp[3]=(node){4,mo(Fp[2].y+f(4))};
 32     Fp[4]=(node){5,mo(Fp[3].y+f(5))};
 33 }
 34 inline int F(int x){
 35     int ans=0; x%=mod;
 36     for(int i=1;i<=4;i++){
 37         int tmp=Fp[i].y;
 38         for(int j=1;j<=4;j++) if(i!=j)
 39             tmp=tmp*(x-Fp[j].x+mod)%mod*inv(Fp[i].x-Fp[j].x+mod)%mod;
 40         ans=mo(ans+tmp);
 41     } return ans;
 42 }
 43 
 44 inline int g(int x){return (m-x)*(x-1)%mod;}
 45 inline void preG(){
 46     Gp[1]=(node){2,g(2)};
 47     Gp[2]=(node){3,mo(Gp[1].y+g(3))};
 48     Gp[3]=(node){4,mo(Gp[2].y+g(4))};
 49     Gp[4]=(node){5,mo(Gp[3].y+g(5))};
 50 }
 51 inline int G(int x){
 52     int ans=0; x%=mod;
 53     for(int i=1;i<=4;i++){
 54         int tmp=Gp[i].y;
 55         for(int j=1;j<=4;j++) if(i!=j)
 56             tmp=tmp*(x-Gp[j].x+mod)%mod*inv(Gp[i].x-Gp[j].x+mod)%mod;
 57         ans=mo(ans+tmp);
 58     } return ans;
 59 }
 60 
 61 inline void preH(int x){
 62     Hp[1]=(node){2,f(2)*G(x-2)%mod};
 63     Hp[2]=(node){3,mo(Hp[1].y+f(3)*G(x-3)%mod)};
 64     Hp[3]=(node){4,mo(Hp[2].y+f(4)*G(x-4)%mod)};
 65     Hp[4]=(node){5,mo(Hp[3].y+f(5)*G(x-5)%mod)};
 66     Hp[5]=(node){6,mo(Hp[4].y+f(6)*G(x-6)%mod)};
 67     Hp[6]=(node){7,mo(Hp[5].y+f(7)*G(x-7)%mod)};
 68     Hp[7]=(node){8,mo(Hp[6].y+f(8)*G(x-8)%mod)};
 69 }
 70 inline int H(int x){
 71     int ans=0; x%=mod;
 72     for(int i=1;i<=7;i++){
 73         int tmp=Hp[i].y;
 74         for(int j=1;j<=7;j++) if(i!=j)
 75             tmp=tmp*(x-Hp[j].x+mod)%mod*inv(Hp[i].x-Hp[j].x+mod)%mod;
 76         ans=mo(ans+tmp);
 77     } return ans;
 78 }
 79 inline int calc(int x){
 80     int ans=0; x/=2;
 81     int lim1=min(x-m,n-1);
 82     int lim2=max(2ll,lim1+1);
 83     int lim3=min(n-1,x-2);
 84     preH(x);
 85     if(lim1>=2) ans=mo(ans+F(lim1)*G(m-1)%mod);
 86     if(lim2<=lim3) ans=mo(ans+H(lim3)-H(lim2-1)+mod);
 87     return ans;
 88 }
 89 namespace WSN{
 90     inline short main(){
 91         freopen("table.in","r",stdin);
 92         freopen("table.out","w",stdout);
 93         n=read(); m=read(); L=read(); R=read();
 94         if(R<8) return puts("0"),0;
 95         v6=qmo(6,mod-2); v2=qmo(2,mod-2);
 96         if(L<=8&&(n+m-2)*2<=R){
 97             int M=m*(m-1)%mod*(m-2)%mod;
 98             int N=n*(n-1)%mod*(n-2)%mod;
 99             write(M*N%mod*v6%mod); return 0;
100         }
101         preF(); preG();
102         int wsn=mo(6ll*calc(R)%mod-6ll*calc(L-1)%mod+mod);
103         write(wsn);
104         return 0;
105     }
106 }
107 signed main(){return WSN::main();}
View Code

T3 黑白

不會,姑姑沽

T4 打怪

比較神仙的$CDQ$分治套單調佇列

處理一個$c_i$表示幹掉一個怪需要多長時間

然後不難發現先打那個攻擊高血條長的怪比較優,考慮按照$a*c$排序

再處理一個$e_i$表示秒殺一個怪的貢獻,

$e_i=a_i\sum_{j=1}^ {i-1}c_j+a_i(c_i-1)+c_i\sum_{j=i+1}^{n}a_j$

在確定秒殺一隻怪$i$的情況下,秒殺$k$怪比秒殺$j$怪更優當且僅當:

$e_i+e_j-a_i*c_j>e_k+e_i-a_i*c_k$

化簡之後是一個斜率式的模樣:

$a_i> \frac {e_j-e_k}{c_j-c_k}$

然後$a_i,c_j$無序,考慮$CDQ$分治,分別在$[L,mid],[mid+1,R]$區間按照$a$,$c$排序

然後單調佇列維護即可

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 namespace AE86{
 5     inline int read(){
 6         int x=0,f=1;char ch=getchar();
 7         while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 8         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
 9     }inline void write(int x,char opt='\n'){
10         char ch[20];int len=0;if(x<0)x=~x+1,putchar('-');
11         do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
12         for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);}
13 }using namespace AE86;
14 
15 const int NN=3e5+5;
16 int n,b,A,C,ans,tot;
17 struct SNOW{
18     int a,c,e;
19 }p[NN];
20 inline bool cmp1(SNOW a,SNOW b){return a.a*b.c>a.c*b.a;}
21 inline bool cmp2(SNOW a,SNOW b){return a.a>b.a;}
22 inline bool cmp3(SNOW a,SNOW b){return a.c<b.c;}
23 int q[NN];
24 inline void merge_sort(int l,int r){
25     if(l>=r) return; int mid=(l+r)>>1;
26     merge_sort(l,mid); merge_sort(mid+1,r);
27     sort(p+l,p+mid+1,cmp3); sort(p+mid+1,p+r+1,cmp2);
28     int h=1,t=0;
29     for(int i=l;i<=mid;i++){
30         while(h<t && (p[q[t]].e-p[q[t-1]].e)*(p[i].c-p[q[t-1]].c)<=(p[i].e-p[q[t-1]].e)*(p[q[t]].c-p[q[t-1]].c) ) --t;
31         q[++t]=i;
32     }
33     for(int i=mid+1;i<=r;i++){
34         while(h<t && p[i].a*(p[q[h+1]].c-p[q[h]].c)<=(p[q[h+1]].e-p[q[h]].e)) ++h;
35         ans=min(ans,tot-p[q[h]].e-p[i].e+p[i].a*p[q[h]].c);
36     }
37 }
38 
39 namespace WSN{
40     inline short main(){
41         freopen("fittest.in","r",stdin);
42         freopen("fittest.out","w",stdout);
43         n=read(); b=read();
44         for(int i=1,a,d,c;i<=n;i++){
45             a=read(); d=read(); c=ceil(1.0*d/b);
46             p[i]=(SNOW){a,c,0}; A+=a;
47         } sort(p+1,p+n+1,cmp1);
48         for(int i=1;i<=n;i++){
49             A-=p[i].a; C+=p[i].c;
50             p[i].e=p[i].a*(C-1)+p[i].c*A;
51             ans+=p[i].a*(C-1);
52         }
53         tot=ans;
54         merge_sort(1,n);
55         write(ans);
56         return 0;
57     }
58 }
59 signed main(){return WSN::main();}
View Code