noip模拟20[玩具·y·z]
noip模拟20 solutions
这次考了105分,主要是第一题之前我做过但是没调出来,挂掉40pts,就挺伤心的,
最重要的是最后一题我现在还没调出来,aaa
以后一定有时间就调他,一定要调出来,补好这个坑!!!
·
T1 玩具
就这个题我一眼就知道是一棵树随意连边,求期望深度
第二眼就想起来我之前做过
第三眼就是我忘记咋做了
然后就苦思冥想,想也想不到哦啊。。。
最后还是有了一点点思路,可是有一个边界卡错了,最后还是只拿了暴力分
其实那也不是正解,只能拿到70pts;
\(O(n^4)\) 1节点原本就在那里,2节点一定会接在1节点上,所以我们所有的节点不是在1子树上就是在2子树上
我们分类讨论,先讨论最大深度在1这个子树上,在讨论在2子树上的时候
注意有可能1,2子树的最大深度相等,这种情况只能算进其中一种里,如果两种都算了这个那就多了
这是统计方案数,从之前的深度转移过来,乘上组合数
设\(f[i][j]\)表示i个点组成的树最大深度为j的方案数,转移就是:
最大深度在1子树:
\(\huge f[i][j]=\sum\limits^{i-1}_{k=1}\sum\limits^{min(k-1,j-2)}_{x=1}f[k][j]×f[i-k][x]×C^{k-1}_{i-2}\)
为啥是这样,我将深度相等的情况放在下一种中处理,所以我们这个时候的最大值一定在1子树,所以f[k][j];
2子树的深度必须小于j-1,所以x从1循环到j-2,组合数代表从i-2个数中选k-1个因为1,2已经固定了
最大深度在2子树,1子树的最大深度可以等于2子树的:
\(\huge f[i][j]=\sum\limits^{i-1}_{k=1}\sum\limits^{min(k-1,j)}_{x=1}f[k][j-1]×f[i-k][x]×C^{k-1}_{i-2}\)
这个和上面是一样的。。。
70pts
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=205;
ll jc[N];
ll yh[N][N];
ll n,mod;
ll f[N][N];
ll ksm(ll x,ll y){
ll ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;
y>>=1;
}
return ret;
}
signed main(){
scanf("%lld%lld",&n,&mod);
jc[1]=1;for(re i=2;i<n;i++)jc[i]=jc[i-1]*i%mod;
for(re i=1;i<=n;i++){
yh[i][0]=1;
yh[i][i]=1;
for(re j=1;j<i;j++){
yh[i][j]=(yh[i-1][j-1]+yh[i-1][j])%mod;
}
}
f[1][0]=1;f[2][1]=1;
for(re i=3;i<=n;i++){
for(re j=1;j<i;j++){
for(re k=1;k<=i-1;k++){//1
for(re x=0;x<=min(k-1,j-2);x++){
f[i][j]=(f[i][j]+f[k][j]*f[i-k][x]%mod*yh[i-2][k-1]%mod)%mod;
}
}
for(re k=1;k<=i-1;k++){//2
for(re x=0;x<=j;x++){
f[i][j]=(f[i][j]+f[k][j-1]*f[i-k][x]%mod*yh[i-2][k-1]%mod)%mod;
}
}
}
}
ll ans=0;
for(re i=1;i<n;i++){
ans=(ans+1ll*i*f[n][i]%mod)%mod;
}
ans=ans*ksm(jc[n-1],mod-2)%mod;
printf("%lld",ans);
}
·
下面的才是正解,\(O(n^3)\),它这个思路极其的叼钻,极其的不好想,但是好理解
那么这个正解是如何得到的呢?
首先我们将这个问题转化为求每一种深度的概率,而这个概率可以用\(n^3\)来求
我们要找到一棵树深度为i的概率,想一想我们找不到所有树的形态,但是所有形态其实就是一个森林
当我们把所有的边都连上,他就变成了一颗树,而且这个过程中可以转移dp,就是正解的思路
这个让我明白了,整个不好求就拆开求
代码有细节,注意当深度大于节点数的时候要赋值为1
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=205;
ll n,mod,jc=1,ans;
ll inv[N];
ll dp[N][N],f[N][N],g[N][N];
ll ksm(ll x,ll y){
ll ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;
y>>=1;
}
return ret;
}
signed main(){
scanf("%lld%lld",&n,&mod);
inv[1]=1;for(re i=2;i<=n;i++){
inv[i]=ksm(i,mod-2);
}
dp[1][1]=1;
for(re i=2;i<=n;i++){
//cout<<i<<" ";
for(re j=1;j<=i;j++){
dp[i][j]=(dp[i-1][j-1]*(j-1)%mod*inv[i]%mod+dp[i-1][j]*(i-j)%mod*inv[i]%mod)%mod;
//cout<<dp[i][j]<<" ";
}
//cout<<endl;
}
for(re i=0;i<=n;i++)f[1][i]=1;
for(re i=0;i<=n;i++)g[0][i]=1;
for(re i=1;i<=n;i++){
for(re j=0;j<i;j++){
for(re k=1;k<=i;k++){
g[i][j]=(g[i][j]+f[k][j]*g[i-k][j]%mod*dp[i][k]%mod)%mod;
}
f[i+1][j+1]=g[i][j];
for(re k=j+1;k<=n;k++)f[i+1][k]=f[i+1][j+1];
if(j==i-1){
for(re k=i;k<=n;k++)g[i][k]=g[i][j];
}
//f[i+1][j+1]=g[i][j];
}
}
//cout<<f[1][1]<<" "<<f[2][1]<<endl;
for(re i=1;i<n;i++){
ans=(ans+i*(f[n][i]+mod-f[n][i-1])%mod)%mod;
//cout<<f[n][i]<<endl;
}
//cout<<inv[jc]<<endl;
printf("%lld",ans);
}
·
T2 y
这个题我搞到了60pts,如何搞到的,用trie树
我用trie树记录每一位后面目前有多少种,就是它的siz
还是利用dfs每扫到一条边,我们就把它加入到trie树中相应的位置,如果此时这个位置已经是一颗满二叉树了,就可以break
复杂度没法算,但是比一般的暴力快。。。
60pts
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=95;
int n,m,d;
int to[N*N*2],nxt[N*N*2],val[N*N*2],head[N],rp;
int sz[N];
void add_edg(int x,int y,int z){
to[++rp]=y;
val[rp]=z;
nxt[rp]=head[x];
head[x]=rp;
}
struct TRIE{
int siz[1<<21],son[1<<21][2],fa[1<<21];
int seg;
TRIE(){seg=1;}
void pushup(int x){
if(!x)return ;
siz[x]++;
pushup(fa[x]);
}
int ins(int x,int v,int dep){
if(siz[son[x][v]]==sz[dep])return 0;
//cout<<"ins "<<x<<" "<<v<<" "<<dep<<endl;
if(!son[x][v]){
son[x][v]=++seg;
fa[seg]=x;
}
if(dep==d){
//cout<<"ok"<<" "<<x<<endl;
siz[son[x][v]]=1;
pushup(x);
}
return son[x][v];
}
}t;
void dfs(int x,int f,int rt,int dep){
if(dep>d)return ;
//cout<<x<<" "<<f<<" "<<rt<<" "<<dep<<endl;
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
int pd=t.ins(rt,val[i],dep);
if(!pd)continue;
dfs(y,x,pd,dep+1);
}
}
signed main(){
scanf("%d%d%d",&n,&m,&d);
int flag=0;
for(re i=1,x,y,z;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add_edg(x,y,z);
add_edg(y,x,z);
if(z!=0)flag=1;
}
if(m==0){
printf("0");
return 0;
}
for(re i=1;i<=d;i++){
sz[i]=1<<(d-i);
}
//cout<<t.seg<<endl;
dfs(1,0,1,1);
printf("%d",t.siz[1]);
}
·
正解竟然是---深搜,没错就是dfs
设f[i][j][s]表示有一条从i出发,从j结束的,状态为s的路径,
我知道你会告诉我,第一维根本没有用,直接从1开始搜不就完事了,确实可以,可是你只有21pts
MEET IN THE MIDDLE这是个好思想,整个做不了就直接给它拆掉,我们重新定义一下数组,根据cty的思想
设f[i][j][s]表示走了i步,走到了j,状态为s
因为我们的起点是1,而终点没有要求,我们只需要枚举到\(\frac{d}{2}\)就好了,
具体是先给f[0][1][0]赋值为真,\(O(2^{\frac{d}{2}}nm)\)找到第\(\frac{d}{2}\)步的终点,
因为终点是没有要求的,第二次我们从后往前找,所有的f[0][i][0]都赋值为真,
所以最后我们三层循环\(O(2^dn)\)复杂度遍历一下就好了
不要用深搜,因为这是图,深搜的最劣复杂度为\(O(n^d)\),直接爆炸
为什么循环的复杂度较小??,因为有许多重复的状态都放到一起了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=95;
int n,m,d;
int to[N*N*2],nxt[N*N*2],val[N*N*2],head[N],rp;
void add_edg(int x,int y,int z){
to[++rp]=y;
val[rp]=z;
nxt[rp]=head[x];
head[x]=rp;
}
int f[25][N][1<<12],f1[N][1<<12],f2[N][1<<12];
signed main(){
scanf("%d%d%d",&n,&m,&d);
for(re i=1,x,y,z;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add_edg(x,y,z);
add_edg(y,x,z);
}
int ans=0,nd=d+1>>1,vd=d>>1;
//memset(f,-1,sizeof(f));
f[0][1][0]=true;
for(re i=0;i<nd;i++){
for(re x=1;x<=n;x++){
for(re j=head[x];j;j=nxt[j]){
int y=to[j];
for(re s=0;s<(1<<i);s++){
f[i+1][y][(s<<1)|val[j]]|=f[i][x][s];
}
}
}
}
for(re i=1;i<=n;i++){
for(re s=0;s<(1<<nd);s++){
f1[i][s]=f[nd][i][s];
//if(f1[i][s])cout<<i<<" "<<s<<endl;
}
}
memset(f,false,sizeof(f));
for(re i=1;i<=n;i++)f[0][i][0]=true;
for(re i=0;i<vd;i++){
for(re x=1;x<=n;x++){
for(re j=head[x];j;j=nxt[j]){
int y=to[j];
for(re s=0;s<(1<<i);s++){
f[i+1][y][(s<<1)|val[j]]|=f[i][x][s];
}
}
}
}
for(re i=1;i<=n;i++){
for(re s=0;s<(1<<vd);s++){
f2[i][s]=f[vd][i][s];
//if(f2[i][s])cout<<i<<" "<<s<<endl;
}
}
for(re i=0;i<(1<<nd);i++){
for(re j=0;j<(1<<vd);j++){
for(re k=1;k<=n;k++){
if(f1[k][i]&&f2[k][j]){
ans++;break;
}
}
}
}
printf("%d",ans);
}
·
T3 z
这个我到如今还没有做,只会一个小暴力
大体思路我会了,但是这个是需要判断起始位置和终止位置的
这个判断极其的麻烦,
要用到map+priority_queue,调试过程会极其复杂,
我先放放,因为昨天做这个的时候做懵了。。。
写完啦写完啦
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define pa pair<int,int>
#define mpa(x,y) make_pair(x,y)
#define fi first
#define se second
const int N=1e5+5;
int n,Q;
int a[N],tas[N],cnt;
ll ans[N];
pa qus[N];
map<int,int> mp;
ll sum;
priority_queue<pa,vector<pa>,greater<pa> > q;
ll sol(ll x){
//cout<<"sol"<<x<<" "<<sum<<endl;
if(mp.empty())return sum;
if(mp.begin()->se<0)return sum-(mp.size()-1)*x;
return sum-mp.size()*x;
}
signed main(){
scanf("%d%d",&n,&Q);
for(re i=1;i<=n;i++)scanf("%d",&a[i]);
for(re i=1;i<=Q;i++)scanf("%d",&qus[i].fi),qus[i].se=i;
int typ=(a[1]>=0?0:1);
for(re i=2;i<=n;i++){
if(a[i]>a[i-1]&&typ==1)tas[++cnt]=a[i-1],typ^=1;
if(a[i]<a[i-1]&&typ==0)tas[++cnt]=a[i-1],typ^=1;
}
tas[++cnt]=a[n];
for(re i=1;i<=cnt;i++){
sum+=abs(tas[i]-tas[i-1]);
mp.insert(mpa(i,tas[i]-tas[i-1]));
//cout<<mp[i]<<" ";
q.push(mpa(abs(tas[i]-tas[i-1]),i));
}
//cout<<endl;
sort(qus+1,qus+Q+1);
int now=1;
while(!q.empty()){
int id=q.top().se,tmp=q.top().fi;q.pop();//find the minnest edge
//cout<<"id:"<<" "<<id<<"tmp:"<<tmp<<endl;
map<int,int>::iterator p=mp.lower_bound(id);
//cout<<p->fi<<" "<<p->se<<endl;
//cout<<mp[4]<<endl;
if(p==mp.end()||p->fi!=id||abs(p->se)!=tmp)continue;//can't find
//cout<<"sb"<<tmp<<endl;
while(now<=Q&&qus[now].fi<tmp)ans[qus[now].se]=sol(qus[now].fi),now++;
//cout<<now<<" "<<qus[now].fi<<" "<<tmp<<endl;
if(p!=mp.begin()){
if(p!=prev(mp.end())){
tmp=p->se;sum-=abs(p->se);
tmp+=prev(p)->se;sum-=abs(prev(p)->se);
tmp+=next(p)->se;sum-=abs(next(p)->se);
mp.erase(prev(p));mp.erase(next(p));
sum+=abs(tmp);
p->se=tmp;
//cout<<p->fi<<" "<<p->se<<endl;
q.push(mpa(abs(tmp),p->fi));
}
else {
sum-=abs(p->se);
mp.erase(p);
}
}
else if(p->se>0){
if(p!=prev(mp.end())){
tmp=p->se;sum-=abs(p->se);
tmp+=next(p)->se;sum-=abs(next(p)->se);
mp.erase(next(p));
if(tmp){// not 0
p->se=tmp;sum+=abs(tmp);
q.push(mpa(abs(tmp),p->fi));
}
else {//000000
mp.erase(p);
}
}
else {
sum-=abs(p->se);
mp.erase(p);
break;
}
}
}
//cout<<now<<" "<<sum<<" "<<prev(mp.end())->se<<endl;
while(now<=Q)ans[qus[now].se]=sol(qus[now].fi),now++;
for(re i=1;i<=Q;i++)printf("%lld\n",ans[i]);
}