Noip模拟54 2021.9.16
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();}
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();}
正解是拉格朗日插值,解决多项式问题,复杂度为多项式次数平方的高级算法
详细解释以及板子可以参考$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();}
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();}