2017.12.09【NOIP提高组】模拟赛A组

2017.12.09【NOIP提高组】模拟赛A组

T1 3489. 【NOIP2013模拟联考11】数列的GCD(gcd)
T2 3500.【NOIP2013模拟联考15】物语(monogatari)
T3 3501.【NOIP2013模拟联考15】消息传递(news)

吐槽:这次的题好像有点水啊,但最简单的第二题都给打挂啦!!(数组开小了)

T1

本套题中最难的题。考虑dp
设f[i]是b[1],b[2]...b[N]的最大公约数的数目,g[i]是b[1],b[2]...b[N]的公约数的数目。
则显然有\(g[i]=\sum_{d|i}f[d]\)
g[i]十分好求。

设l[i]为{a[N]}中满足 i|a[j] (1<=j<=N)的数目

\[g[d]={l[d] \choose n-k}\lfloor\frac{m}{d}\rfloor^{n-l[d]}(\lfloor\frac{m}{d}\rfloor-1)^{l[d]-n+k} \]

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,x) for(int i=head[x];i;i=next[i])
#define mem(a,x) memset(a,x,sizeof(a))
typedef long long LL;
typedef double DB;
using namespace std;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') f=(ch=='-')?-1:f,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();return f*x;
}
const int n=300010,mo=1e9+7;
int a[n],cnt[n],ji[n];
LL fac[n],ans[n];
LL qpow(LL a,int b) {
	a=a%mo;
	LL ans=1;
	for(;b;a=(a*a)%mo,b>>=1) if(b&1) ans=(ans*a)%mo;
	return ans;
}
LL C(int m,int n) {return fac[n]*qpow(fac[m],mo-2)%mo*qpow(fac[n-m],mo-2)%mo;}
int main() {
	int n=read(),m=read(),k=read();
	k=n-k;
	fo(i,1,n) a[i]=read(),ji[a[i]]++;
	fd(i,m,1) for(int j=i;j<=m;j+=i) cnt[i]=cnt[i]+ji[j];
	fac[0]=1;
	fo(i,1,n) fac[i]=((LL)fac[i-1]*i)%mo;
	fd(i,m,1) {
		if(cnt[i]<k) ans[i]=0;
		else {
			ans[i]=C(k,cnt[i])*qpow(m/i-1,cnt[i]-k)%mo*qpow(m/i,n-cnt[i])%mo;
			fo(j,2,m/i) ans[i]=(ans[i]+mo-ans[i*j])%mo;
		}
	}
	fo(i,1,m) printf("%lld ",ans[i]);
	return 0;
}

T2

最简单的题。

只用考虑经不经过这个点即可,经过可分为两种情况:

将就着看一下吧,正反做一遍dij就行了。

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define pr pair<LL, int>
#define mp make_pair
#define x first
#define y second
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,x) for(int i=head[x];i;i=g[i].next)
#define mem(a,x) memset(a,x,sizeof(a))
typedef long long LL;
typedef double DB;
using namespace std;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') f=(ch=='-')?-1:f,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();return f*x;
}
const LL INF=0x7ffffffffffff;
const int N=5e5+50,M=5e5+50;
struct Edge {int x,next;LL dis;} g[M*2] ;
int n,m,k,begin,end,tx,ty,ww;
LL G[N],F[N];
int head[N],w,vis[N];
priority_queue<pr>q;
void add(int x,int y,LL dis) {g[++w].x=y,g[w].next=head[x],g[w].dis=dis,head[x]=w;}
void dij(int x) {
	int tmp;
	LL now;
	fo(i,1,n) F[i]=INF;
	F[x]=0,q.push(mp(0,x));
	while(!q.empty()) {
		x=q.top().y;
		LL now=q.top().x;q.pop();
		if(-now>F[x]) continue;
		now=-now;
		rep(i,x) if(F[tmp=g[i].x]>F[x]+g[i].dis) F[tmp]=F[x]+g[i].dis,q.push(mp(-F[tmp],tmp));
	}
}
int main() {
//	freopen("2.in","r",stdin);
	freopen("monogatari.in","r",stdin),freopen("monogatari.out","w",stdout);
	n=read(),m=read(),k=read();
	fo(i,1,m-1) {tx=read(),ty=read(),scanf("%lld",&ww),add(tx,ty,ww),add(ty,tx,ww);}
	scanf("%d%d",&begin,&end);
	dij(1);
	memcpy(G,F,sizeof(G));
	dij(n);
	fo(i,1,k) {
		scanf("%lld",&ww);
		LL tmp=min(G[n],min(G[begin]+F[end],G[end]+F[begin])+ww);
		if(tmp==INF) printf("+Inf\n") ;
		else printf("%lld\n",tmp) ;
	}
}

T3

考虑树形dp,明显要旋根,记录每个点儿子的前缀和后缀最大值。
随便搞一下就行了。(很简单,相出dp就行了)

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,x) for(int i=head[x];i;i=next[i])
#define mem(a,x) memset(a,x,sizeof(a))
typedef long long LL;
typedef double DB;
using namespace std;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') f=(ch=='-')?-1:f,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();return f*x;
}
const int mn=401000;
int n,tt=1,fa[mn],head[mn],next[mn*2],to[mn*2];
int f[mn],g[mn],sum[mn],ans[mn],u[mn],q[mn],cnt=0;
int pre[mn],sa[mn],tot;
void add(int i,int j) {next[++tot]=head[i],head[i]=tot,to[tot]=j;}
bool cmp(int i,int j) {return sum[i]<sum[j];}
void solve() {
	int a=1,b=1;
	q[1]=1;
	while(a<=b) {
		int now=q[a++];
		rep(p,now) if(to[p]!=fa[now]) q[++b]=to[p];
	}
	fd(i,n,1) {
		int now=q[i],tt=0;
		rep(p,now) if(to[p]!=fa[now]) u[++tt]=f[to[p]];
		sort(u+1,u+1+tt);
		fo(j,1,tt) f[now]=max(f[now],u[j]+tt-j+1);
	}
	ans[1]=f[1];
	fo(i,1,n) {
		int now=q[i],tt=0;
		rep(p,now) if(to[p]!=fa[now]) u[++tt]=to[p],sum[to[p]]=f[to[p]];
		if(now!=1) u[++tt]=fa[now],sum[fa[now]]=g[now];
		sort(u+1,u+1+tt,cmp);
		pre[0]=sa[tt+1]=0;
		fo(j,1,tt) ans[now]=max(ans[now],sum[u[j]]+tt-j+1);
		fo(j,1,tt) pre[j]=max(pre[j-1],sum[u[j]]+tt-j);
		fd(j,tt,1) sa[j]=max(sa[j+1],sum[u[j]]+tt-j+1);
		fo(j,1,tt) if(u[j]!=fa[now]) g[u[j]]=max(pre[j-1],sa[j+1]);
	}
}
int main() {
	freopen("news.in","r",stdin),freopen("news.out","w",stdout);
	n=read();
	fo(i,2,n) fa[i]=read(),add(fa[i],i),add(i,fa[i]);
	int tmp=1<<30;
	solve();
	fo(i,1,n) tmp=min(tmp,ans[i]);
	printf("%d\n",tmp+1);
	fo(i,1,n) if(ans[i]==tmp) printf("%d ",i);
	bool first=1;
	return 0;
}
posted @ 2017-12-09 15:56  _patrick  阅读(202)  评论(0编辑  收藏  举报