HL省选冲刺
D-8
申请停课,ly说的还是很有道理的(虽然我也这么想),文化课什么的,早晚都得学,oi可能就学不了了(tcl),想做什么还是去做吧,也没什么不可能的(是吧,陈。 :) )做了准备(虽然并不adequate,但是不尝试=100%GG,尝试=99%GG)有计划每天上午考试下午改题晚上改题。结果已经不重要了(学2年了快,挺有趣的)
D-7
停课D1,早自习了。上午各科老师问我干啥了,尴尬死。。。我以为ly说过了,给各位造成了不便。。。sorry。上午考试发现自己菜死了。sb题都不会了,analysis below
T1
(其他样例啥的就不写了,子任务)
先分析一下,一开始觉得期望,就没怎么看,后来看到了子任务有 只有一个边有权值,暗示我们这个题只需要一个一个边考虑就行。首先对于一条边,左边x*boy,y*girl,(右边m-x*boy,m-y*girl)然后左边子树size,(右边子树n-size)我们肯定想最大,最大的话就是min(x,m-y)+min(y,m-x) 这东西画画图发现=max(x+y,2m-x-y)
实际上问题就变成了,2m中选x个人(不分男女)然后瞎jb放
还有个点就是:根据调整法(我这么叫他)我们肯定能在保证一条边达到最大贡献 ,同时 ,让另一条边也达到最大 贡献,然后推广到所有边。
然后问题就愉快的变成了 sigma w[u,v]*c(2m,x)*size[u]^x*size[v]^(2m-x)*max(x,2m-x) (x=1~2m-1)瞎jb选x个人,瞎jb放在一边,然后瞎jb连边达到最大,然后就可以了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 const int maxn=2505; 5 const LL P=1e9+7ll; 6 int Pow[maxn][maxn<<1];//pow[i][j]=i^j 7 int sum[maxn];//sum[i]=sigma C(2m,j)i^j*(n-i)^(2m-j) (j=1~2m-1) 8 int n,m; 9 int w[maxn]; 10 struct Edge{ 11 int u,v; 12 Edge(int U=0,int V=0):u(U),v(V){} 13 }e[maxn]; 14 int ans; 15 int fst[maxn],nxt[maxn<<1],to[maxn<<1],edge_count; 16 inline void add(int x,int y){ 17 edge_count++; 18 to[edge_count]=y; 19 nxt[edge_count]=fst[x]; 20 fst[x]=edge_count; 21 } 22 int size[maxn]; 23 void dfs(int u,int fa){ 24 size[u]=1; 25 for(int i=fst[u];i;i=nxt[i]){ 26 int v=to[i]; 27 if(v==fa)continue; 28 dfs(v,u); 29 size[u]+=size[v]; 30 } 31 } 32 int jc[maxn<<1],inv[maxn<<1],njc[maxn<<1]; 33 inline void com(){ 34 inv[1]=jc[1]=njc[1]=jc[0]=njc[0]=1; 35 for(int i=2;i<=m<<1;i++){ 36 jc[i]=1ll*jc[i-1]*i%P; 37 inv[i]=1ll*(P-P/i)*inv[P%i]%P; 38 njc[i]=1ll*njc[i-1]*inv[i]%P; 39 } 40 } 41 inline int c(int n,int m){return 1ll*jc[n]*njc[m]%P*njc[n-m]%P;} 42 inline void init(){ 43 for(int i=1;i<n;i++){//ö¾Ù size & n-size 44 Pow[i][0]=1; 45 for(int j=1;j<m<<1;j++)Pow[i][j]=1ll*Pow[i][j-1]*i%P; 46 //size^(x+y) & size^(2m-x-y) 47 } 48 for(int i=1;i<n;i++){//sum[size]=sigma size^(x+y)*(n-size)^(2m-x-y) (x+y = 1~2m-1) 49 for(int j=1;j<m<<1;j++) 50 sum[i]=(1ll*sum[i]+1ll*c(m<<1,j)*min(j,(m<<1)-j)%P*Pow[i][j]%P*Pow[n-i][(m<<1)-j]%P)%P; 51 } 52 } 53 int main(){ 54 scanf("%d%d",&n,&m); 55 com();init(); 56 for(int i=1,u,v;i<n;i++){ 57 scanf("%d%d%d",&u,&v,&w[i]); 58 e[i]=Edge(u,v); 59 add(u,v);add(v,u); 60 } 61 dfs(1,0); 62 for(int i=1;i<n;i++){ 63 ans=(1ll*ans+1ll*w[i]*sum[ min(size[e[i].u],size[e[i].v]) ])%P; 64 } 65 66 printf("%d",ans); 67 return 0; 68 }
T2
挺sb的题,但是我想了好久,一开始发现,倍长(显然),枚举,判定,问题是如何判定,一直在想,如果 k 不合法,那么会在 相邻两个不相同的情况下 构成 f(k)个,相隔k个出现相同字符,(aaaaaaabaab,k=3(k的定义是,旋转的长度))然后想的是建边,跑topological sort,但是没办法,边长太多,不能直接搞。
经过qzh dalao启发,hash,冥思苦想 啊,hash判定字符串相等(重新推了一下 unsigned long long 自然溢出hash,真tm好用。),发现,刚刚自己一直想弄的,有f(k)个不合法子串<=>平移k位 重合。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
数位DP(入门~?)
以数位为阶段的DP。定义啥的上网找找,仅仅作为题集。
T1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 LL a,b; 5 LL f[50][20];//f[i][j]表示构成 第i位为j的方案数 6 LL sta[50],p; 7 LL dfs(LL p,LL num,bool lim,bool zero){//表示上一位 为num,该位为 p ,是否有限制 是否连续为0 的方案数 8 if(!p)return !zero;//非零 & 确定每一位 9 if(!lim && !zero && f[p+1][num]!=-1)return f[p+1][num]; 10 LL Max=(lim ? sta[p] : 9);LL ans=0; 11 for(LL i=0;i<=Max;i++){ 12 if(abs(i-num)>=2 || zero)ans+=dfs(p-1,i,lim&&(i==sta[p]),zero&&(i==0)); 13 } 14 if(!lim && !zero){ 15 f[p+1][num]=ans; 16 } 17 return ans; 18 } 19 inline LL solve(LL n){ 20 if(n<10)return n; 21 p=0; 22 while(n){ 23 sta[++p]=n%10; 24 n/=10; 25 } 26 return dfs(p,0,1,1); 27 } 28 int main(){ 29 scanf("%lld%lld",&a,&b); 30 memset(f,-1,sizeof f); 31 printf("%lld\n",solve(b)-solve(a-1)); 32 return 0; 33 }
T2~T?(later)
数据结构combination:
T1:套路题
给定一个长度为偶数的排列 p, 以及一个初始时为空的序列 q, 对其进行如下操作直 到 p 为空: 从 p 中找出两个相邻元素,按原来在 p 中的顺序加入 q 头部,然后把它们从 p 中 删去。 求可能得到的字典序最小的 q。
首先(这种题)可以显然的知道,如果一个数x在p奇数位置,那么它肯定可能成为q的首位,那么有个显然的结论就是,每次拿出的两个数,必定不会跨过x,所以把剩下的 序列分成两部分,右边的部分必须有一个(相对于新序列)奇数位的数y ,和x配对一起拿出来,然后又分裂成两个序列,所以我们可以用堆维护一下奇数位最小值 最小的 序列,用平衡树维护每个序列,(有个技巧就是,可以每次维护奇数位最小值 偶数位最小值 ,然后 记录一下这个序列是取奇数位还是偶数位)
ps:挺好的题,很久不写平衡树了(1h写,1h调)。
code:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=2e5+5; 4 int a[maxn],pos[maxn]; 5 struct Node{ 6 int val,num,ls,rs,rd,size,mn[2];//0->even(żÊý) , 1-> odd(ÆæÊý) 7 Node(int Val=0,int Num=0,int Ls=0,int Rs=0,int Rd=0,int Size=0):val(Val),num(Num),ls(Ls),rs(Rs),rd(Rd),size(Size){} 8 }t[maxn]; 9 inline void maintain(int x){ 10 t[x].size=t[ t[x].ls ].size + t[ t[x].rs ].size+1; 11 t[x].mn[0]=min(t[ t[x].ls ].mn[0],t[ t[x].rs ].mn[0]); 12 t[x].mn[1]=min(t[ t[x].ls ].mn[1],t[ t[x].rs ].mn[1]); 13 t[x].mn[x&1]=min(t[x].mn[x&1],t[x].num); 14 } 15 void split(int x,int val,int &r1,int &r2){//[0,val] [val,inf] 16 if(!x){r1=r2=0;return;} 17 if(val>=t[x].val){ 18 r1=x; 19 split(t[x].rs,val,t[x].rs,r2); 20 } 21 else{ 22 r2=x; 23 split(t[x].ls,val,r1,t[x].ls); 24 } 25 maintain(x); 26 } 27 int merge(int r1,int r2){ 28 if(!r1 || !r2)return r1+r2; 29 if(t[r1].rd < t[r2].rd){ 30 t[r1].rs=merge(t[r1].rs,r2); 31 maintain(r1); 32 return r1; 33 } 34 else { 35 t[r2].ls=merge(r1,t[r2].ls); 36 maintain(r2); 37 return r2; 38 } 39 } 40 int n,root; 41 priority_queue< pair < int , int > ,vector < pair < int ,int > > ,greater< pair < int ,int > > > q; 42 bool vis[maxn]; 43 inline void insert(int r){ 44 int Min=t[r].mn[1 ^ vis[r]]; 45 q.push(make_pair(Min,r)); 46 } 47 inline void solve(int r){ 48 q.pop(); 49 int pm=pos[ t[r].mn[1^vis[r]] ];printf("%d ",a[pm]); 50 int r1,r2,r3,r4,r5,r6; 51 split(r,pm-1,r1,r2); 52 if(r1){ 53 vis[r1]=vis[r]; 54 insert(r1); 55 } 56 split(r2,pm,r2,r3); 57 vis[r3]=!vis[r]; 58 pm=pos[ t[r3].mn[1^vis[r3]] ];printf("%d ",a[pm]); 59 split(r3,pm-1,r4,r5); 60 if(r4){ 61 vis[r4]=vis[r3]; 62 insert(r4); 63 } 64 split(r5,pm,r5,r6); 65 if(r6){ 66 vis[r6]=!vis[r3]; 67 insert(r6); 68 } 69 } 70 int main(){ 71 srand(time(0)); 72 scanf("%d",&n); 73 t[0].mn[0]=t[0].mn[1]=maxn; 74 for(int i=1;i<=n;i++){ 75 scanf("%d",&a[i]); 76 t[i]=Node(i,a[i],0,0,rand(),1); 77 t[i].mn[0]=t[i].mn[1]=maxn; 78 t[i].mn[i&1]=a[i]; 79 pos[a[i]]=i; 80 root=merge(root,i) ; 81 } 82 insert(root); 83 for(int i=1;i<=(n>>1);i++){ 84 int rr=q.top().second; 85 solve(rr); 86 } 87 return 0; 88 }
稍有思维:
T1:
小 B 有一个巨大的棋盘,你可以把它看作一个无限大的二维坐标系。
棋盘上有 N 个棋子, 每个棋子占一个格子,每个格子上至多只有一枚棋子。
小 B 还可以再在棋盘上下 K 个棋子,问有多少个不同的点,使得存在一种下法, 这个点可以成为所有棋子的对称中心 (对称中心可以不是格点)。
若这样的对称中心存在无穷多个,输出 −1。 棋子只能下在格点处。
这个题,qzh说挺水的。首先一个简单地思路就是枚举两个点,然后算出对称中心(x1+x2,y1+y2)就不/2了,然后枚举每个点判断是否有对称点,没有就记录,判断需要多少个新棋子
那么这个显然是超时的,既然k个新棋子可以消除k个当前棋子的影响,那么我们把所有的点按横坐标为第一关键字,纵坐标为第二关键字排序,左下k+1和右上k+1至少有一对匹配点。
枚举这对匹配点再进行判定就会减少很多复杂度(k<=20,k^2<=400)然后我一开始用了map套map暴力验证。实际上发现,排完序之后,点P[l]与点P[r]的对称中心,如果偏向 左边,那么r一定没有对称中心,反之亦然。
双指针判定一下就好。判定复杂度降为O(nlogn)->O(n)【ps:代码不粘了。自己应该想到的。】
DP:
T1:「CQOI2017」老 C 的键盘
题意,给出一科完全儿叉树上父亲儿子的大小关系,问有多少种排列满足。
首先计数问题,应该想到dp,加上前几天做过的数位dp([ZJOI2010]排列计数https://www.luogu.com.cn/problem/P2606此题是满足堆性质的排列个数,因为一个堆,如果元素数量一定,不管元素是什么,构成方案数一定)本题我一开始设状态f[i][j]表示以i为根的子树 ,i选 数j 时的方案数,但是发现无法转移(因为谁也不知道其他的点放了啥,和堆还不一样)但是我们想这样一个问题(qzh):
对于这棵树,我们如果规定某种大小关系,求出拓扑序,那么这个拓扑序和树本身一一对应,或者说,和元素本身没关系,只和相对大小有关。所以我们仍然设f[i][j]为状态,但是不同的是:f[i][j]表示以i为根的子树,i在子树内rank 为j的方案数,巧妙地利用了刚刚的性质,还方便转移。所以我们直接树dp,每次枚举rank,枚举当前儿子会对rank产生多少贡献,最后利用题目限制条件,进行转移。由于n很小所以不用前缀or后缀和也是可以的。(据说枚举子树大小是O(logn)的)
code:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int P=1e9+7; 4 const int maxn=1e2+5; 5 int c[maxn][maxn],f[maxn][maxn],size[maxn],temp[maxn][maxn]; 6 //f[i][j]±íʾÔÚµãiµÄ×ÓÊ÷ÖУ¬iµÄrankΪj 7 int n; 8 char ch[maxn]; 9 void dfs(int u){ 10 size[u]=1; 11 f[u][1]=1; 12 for(int v=(u<<1);v<=min(n,u<<1|1);v++){ 13 dfs(v); 14 size[u]+=size[v]; 15 for(int j=1;j<=size[u];j++) 16 for(int k=0;k<=j;k++){ 17 if(ch[v]=='<'){ 18 for(int l=k+1;l<=size[v];l++) 19 temp[u][j]=(temp[u][j]+1ll*c[j-1][k]*c[size[u]-j][size[v]-k]%P*f[v][l]%P*f[u][j-k]%P)%P; 20 } 21 else { 22 for(int l=0;l<=k;l++) 23 temp[u][j]=(temp[u][j]+1ll*c[j-1][k]*c[size[u]-j][size[v]-k]%P*f[v][l]%P*f[u][j-k]%P)%P; 24 } 25 } 26 for(int j=1;j<=size[u];j++){ 27 f[u][j]=temp[u][j]; 28 temp[u][j]=0; 29 } 30 } 31 } 32 inline void init(){ 33 for(int i=0;i<=maxn-5;i++)c[i][0]=1; 34 for(int i=1;i<=maxn-5;i++)for(int j=1;j<=i;j++) 35 c[i][j]=(c[i-1][j]+c[i-1][j-1])%P; 36 } 37 int main(){ 38 scanf("%d%s",&n,ch+2); 39 init(); 40 dfs(1); 41 int ans=0; 42 for(int i=1;i<=n;i++)ans=(ans+f[1][i])%P; 43 printf("%d",ans); 44 return 0; 45 }
T2: