noip模拟8
A. 星际旅行
将所有的边拆成两条,问题变成去掉两条边,使得原图存在一条欧拉路径。注意新图中所有点的度数均为偶数,只需按照去掉任意2个自环、去掉任意1个自环和任意一条边、去掉两条有公共顶点的边进行讨论即可。注意图不连通的判断方式,不是点不连通,而是边不连通..
本应就是一道规律题吧..
A_code
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define lf double
const ll N=1e7+50;
inline void read(ll &ss)
{
ss=0;
ll cit=0;
char ch;
ch=getchar();
while((ch>'9') or (ch<'0'))
{
if(ch=='-') cit=1;
ch=getchar();
}
while((ch<='9') and (ch>='0'))
{
ss=(ss<<3)+(ss<<1)+(ch^48);
ch=getchar();
}
if(cit) ss=-ss;
}
ll n,m,ts,alls,num,ans,cnt;//num代表回环边的数量,cnt代表普通边的数量
ll vis[N],head[N],d[N];
struct Role{ ll u,v,w,nxt; } a[N*2];
inline void add(ll u,ll v)
{
a[++ts].u=u;
a[ts].v=v;
a[ts].nxt=head[u];
head[u]=ts;
}
void dfs(ll now)
{
vis[now]=1;
for(ll i=head[now];i;i=a[i].nxt)
{
if(!vis[a[i].v])
{
dfs(a[i].v);
}
}
}
signed main()
{
read(n); read(m);
ll u,v;
for(ll i=1;i<=m;i++)
{
read(u); read(v);
if(u==v) { num++; continue; }
d[u]++; d[v]++;
add(u,v); add(v,u);
}
cnt=m-num;
if(cnt==0) { putchar('0'); return 0;}
for(ll i=1;i<=n;i++) if(d[i]) { dfs(i);break; }
for(ll i=1;i<=n;i++) if(d[i] and vis[i]==0) { putchar('0'); return 0;}
ans+=((num-1)*num)/2;
for(ll i=1;i<=n;i++) ans+=(d[i]*(d[i]-1))/2;
ans+=num*cnt;
printf("%lld",ans);
return 0;
}
B. 砍树
根据整除分块原理计算即可.
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define re register ll
#define ull unsigned ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x);
#define Copy(x,y) memcpy(x,y,sizeof x);
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=111,M=sqrt(1e9+1);
ll n,m,cnt,sum,ans,C;
ll w[N],d[N*M];
signed main(){
n=read(),m=read();
for(re i=1;i<=n;i++) w[i]=read(),sum+=w[i];
C=m+sum;
for(ll l=1,r;l<=C;l=r+1) d[++cnt]=r=C/(C/l);
ll res;
for(re i=1;i<=cnt;i++){
res=0;
for(re j=1;j<=n;j++) res+=w[j]/d[i]+(w[j]%d[i]>0);
if(res*d[i]<=C) ans=d[i];
}
printf("%lld",ans);
return 0;
}
C. 超级树
考虑\(dp_{i,j}\)表示一棵i-超级树,有j条点不重复的路径的方案数。考虑\(dp_i\)对\(dp_{i+1}\)的贡献:枚举左子树和右子树的路径条数\(l\)、\(r\),记\(num=dp_{i,l}*dp_{i,r}\),则有
-
什么也不做 \(dp[i+1][l+r]+=num\)
-
根自己作为一条新路径 \(dp[i+1][l+r+1]+=num\)
-
根连接到左子树(或右子树)的某条路径上 \(dp[i+1][l+r]+=2*num*(l+r)\)
-
根连接左子树和右子树的各一条路径 \(dp[i+1][l+r-1]+=2*num*l*r\)
-
根连接左子树(或右子树)的两条路径 \(dp[i+1][l+r-1]+=num*(l*(l-1)+r*(r-1))\)
边界为\(dp[1][0]=dp[1][1]=1\),答案为\(dp[k][1]\)。看起来第二维状态可能有\(2*k\) 那么大,但注意到从\(dp[i]\)转移到\(dp[i+1]\)时,路径的条数最多减少\(1\)条,因此第二维只有\(k\)个状态对最终的状态有影响,只\(dp\)这些状态即可.
复杂度为 \(O(k^3)\)
C_code
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define lf double
inline void read(ll &ss)
{
ss=0;
ll cit=0;
char ch;
ch=getchar();
while((ch>'9') or (ch<'0'))
{
if(ch=='-') cit=1;
ch=getchar();
}
while((ch<='9') and (ch>='0'))
{
ss=(ss<<3)+(ss<<1)+(ch^48);
ch=getchar();
}
if(cit) ss=-ss;
}
//什么也不做--1 根自己作为一条新路径--2 根与子树相连--3
//左与右相连--4 左/右自己相连--5
ll n,mod,num;
ll f[500][500];
signed main()
{
read(n); read(mod);
f[1][1]=1; f[1][0]=1;
for(ll i=0;i<=n;i++)
{
for(ll j=0;j<=n;j++)
{
for(ll l=0;l<=j;l++)
{
num=f[i][(j-l)]*f[i][l]%mod;
f[i+1][j]+=num; f[i+1][j]%=mod;
f[i+1][j+1]+=num; f[i+1][j+1]%=mod;
f[i+1][j]+=num*j*2; f[i+1][j]%=mod;
f[i+1][j-1]+=num*l*(j-l)*2; f[i+1][j-1]%=mod;
f[i+1][j-1]+=num*(l*(l-1)+(j-l)*(j-l-1)); f[i+1][j-1]%=mod;
}
}
}
printf("%lld",f[n][1]%mod);
return 0;
}
D. 求和
预处理出所有k次方和所有节点的深度,树上差分即可..
D_code
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
#define lf double
const ll N=300050;
const ll mod=998244353;
inline void read(ll &ss)
{
ss=0;
ll cit=0;
char ch;
ch=getchar();
while((ch>'9') or (ch<'0'))
{
if(ch=='-') cit=1;
ch=getchar();
}
while((ch<='9') and (ch>='0'))
{
ss=(ss<<3)+(ss<<1)+(ch^48);
ch=getchar();
}
if(cit) ss=-ss;
}
ll n,t,ts,m,maxn,ans,k;
ll fa[N][50];
ll dep[N],head[N],down[N][55],f[N][55];
struct Summary{ ll u,v,w,nxt; } a[N*2];
inline void add(ll u,ll v)
{
a[++ts].u=u;
a[ts].v=v;
a[ts].nxt=head[u];
head[u]=ts;
}
void dfs(ll now,ll dad,ll depth)
{
maxn=max(maxn,depth);
dep[now]=depth;
fa[now][0]=dad;
for(ll i=1;i<=t;i++)
{
if(fa[now][i-1]==0) break;
fa[now][i]=fa[fa[now][i-1]][i-1];
}
for(ll i=head[now];i;i=a[i].nxt)
{
if(a[i].v==dad) continue;
dfs(a[i].v,now,depth+1);
}
return ;
}
inline ll lca(ll x,ll y)
{
if(dep[x]<dep[y]) swap(x,y); //保证 x 在 y 的下面..
for(ll i=t;i>=0;i--)
{
if(dep[fa[x][i]]>=dep[y] and fa[x][i]!=0) x=fa[x][i];
} //使达到同一深度..
if(x==y) return x;
for(ll i=t;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i]; y=fa[y][i];
}
}
return fa[x][0];
}
signed main()
{
read(n);
t=(ll)(log(n)/log(2))+1;
ll u,v,w;
for(ll i=1;i<=n-1;i++)
{
read(u); read(v);
add(u,v); add(v,u);
}
dfs(1,0,0);
for(ll i=1;i<=n;i++)
{
down[i][0]=1;
for(ll j=1;j<=51;j++)
{
down[i][j]=(down[i][j-1]*i)%mod;
f[i][j]=(f[i-1][j]+down[i][j])%mod;
}
}
read(m);
ll anor;
for(ll i=1;i<=m;i++)
{
read(u); read(v); read(k);
anor=lca(u,v);
//cout<<anor<<" depth:"<<dep[u]<<" "<<dep[v]<<" "<<dep[anor]<<endl;
//cout<<f[dep[u]][k]<<" "<<f[dep[v]][k]<<endl;
ans=f[dep[u]][k]+f[dep[v]][k]-2*f[dep[anor]][k]+down[dep[anor]][k];
ans=(ans+mod*mod)%mod;
printf("%lld\n",ans);
}
return 0;
}