2019-10-5 NOIP模拟赛多校联考——Round7 题解

T1 整数分解

裸的完全背包……被孙到
2 i 2^i 2i当作物品,容量为 n n n

#include
#include
#include
#include
#define re register int
#define mod 1000000007
using namespace std;
typedef long long ll;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x*f;
}
const int Size=1000005;
int n,dp[Size],LOG[Size];
int main() {
//	freopen("T1.in","r",stdin);
	n=read();
	dp[0]=1;
	for(re i=0; i<=20; i++) {
		for(re j=0; j+(1<<i)<=n; j++) {
			dp[j+(1<<i)]=(dp[j+(1<<i)]+dp[j])%mod;
		}
	}
	printf("%d",dp[n]);
	return 0;
}

T2 贫富差距
u , v u,v u,v是朋友就建一条 u , v u,v u,v的边。
如果所有的点都在同一个联通块中,那么贫富差距的最大值就是最长链的长度乘以 d d d(易证无法构造出更大的贫富差距)。
判-1用并查集,判是否所有的点在同一个联通块中即珂,如果有两个点不在同一个联通块中,则这两个点的贫富差距珂以无限扩大。

#include
#include
#include
#include
#define re register int
#define mod 1000000007
using namespace std;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x*f;
}
inline char GetChar() {
	char ch=getchar();
	while(ch!='Y' && ch!='N')	ch=getchar();
	return ch;
}
const int Size=105;
int n,d,father[Size];
int Find(int x) {
	if(x==father[x])	return x;
	return father[x]=Find(father[x]);
}
int cnt,head[Size];
struct node {
	int v,next;
} w[Size*Size<<1];
inline void AddEdge(int u,int v) {
	w[++cnt].v=v;
	w[cnt].next=head[u];
	head[u]=cnt;
}
int Queue[Size],dis[Size];
bool vis[Size];
int bfs(int s) {
	memset(vis,0,sizeof(vis));
	int hd=0,tl=0;
	Queue[++tl]=s;
	vis[s]=true;
	dis[s]=0;
	int ans=0;
	while(hd<tl) {
		int x=Queue[++hd];
		for(re i=head[x]; i; i=w[i].next) {
			int nxt=w[i].v;
			if(!vis[nxt]) {
				vis[nxt]=true;
				Queue[++tl]=nxt;
				dis[tl]=dis[hd]+1;
				if(dis[tl]>ans) {
					ans=dis[tl];
				}
			}
		}
	}
	return ans;
}
inline void init() {
	memset(head,0,sizeof(head));
	cnt=0;
	for(re i=1; i<=n; i++) {
		father[i]=i;
	}
}
int main() {
//	freopen("T2.in","r",stdin);
	int T=read();
	while(T--) {
		n=read();
		d=read();
		init();
		for(re i=1; i<=n; i++) {
			for(re j=1; j<=n; j++) {
				char ch=GetChar();
				if(ch=='Y') {
					AddEdge(i,j);
					int fx=Find(i);
					int fy=Find(j);
					if(fx!=fy) {
						father[fx]=fy;
					}
				}
			}
		}
		int fa=Find(1);
		bool inf=false;
		for(re i=2; i<=n; i++) {
			if(Find(i)!=fa) {
				inf=true;
				break;
			}
		}
		if(inf) {
			puts("-1");
			continue;
		}
		int ans=0;
		for(re i=1; i<=n; i++) {
			int now=bfs(i);
			if(now>ans) {
				ans=now;
			}
		}
		printf("%d\n",ans*d);
	}
	return 0;
}

T3 特殊的排列
找出序列上相邻两项差为1的最长子序列,则这个子序列长度是最长的不用移动的长度。
证明:
1.移动一个子序列之外的数,子序列的相对顺序不变。
2.不在最长子序列中的数一定要移动到序列首或序列末(否则珂以形成更长的相邻两项差为1的子序列)。
3.不存在另一个更长的子序列,相邻两项差不为1,且这个子序列中的数不用移动。因为若相邻两项差不为1,那么这两项之间的数必须要通过移动子序列里的数来得到。

#include
#include
#include
#include
#define re register int
using namespace std;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
const int Size=50005;
int dp[Size];
int main() {
	int n=read();
	int ans=0;
	for(re i=1; i<=n; i++) {
		int x=read();
		if((dp[x]=dp[x-1]+1)>ans) {
			ans=dp[x];
		}
	}
	printf("%d",n-ans);
	return 0;
}

T4 极品飞车
一个特殊的隐含条件是速度必须要>0,否则时间会变成负数或正无穷(长者:只有我才能操控负数和 + ∞ +\infin +的时间,你们还是太naive)
列出式子, d 1 s 1 + c + d 2 s 2 + c + . . . + d n s n + c = t \frac{d_1}{s_1+c}+\frac{d_2}{s_2+c}+...+\frac{d_n}{s_n+c}=t s1+cd1+s2+cd2+...+sn+cdn=t
因为对于任意 i i i,有 s i + c > 0 s_i+c>0 si+c>0,所以左边是单调递减的。
所以二分 c c c,暴力判即珂。

#include
#include
#include
#include
#define re register int
#define mod 1000000007
#define eps 1e-10
using namespace std;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x*f;
}
inline char GetChar() {
	char ch=getchar();
	while(ch!='Y' && ch!='N')	ch=getchar();
	return ch;
}
const int Size=1005;
int n;
double t,s[Size],d[Size];
double check(double mid) {
	double ans=0;
	for(re i=1; i<=n; i++) {
		if(fabs(s[i]+mid)<eps) {
			ans+=1e15;
		} else {
			ans+=d[i]/(s[i]+mid);
		}
	}
	return ans;
}
int main() {
//	freopen("T4.in","r",stdin);
	n=read();
	scanf("%lf",&t);
	double minn=1e10;
	for(re i=1; i<=n; i++) {
		scanf("%lf %lf",&d[i],&s[i]);
		if(s[i]<minn)	minn=s[i];
	}
	double l=-minn+eps,r=1e7,mid;
	for(re i=1; i<=1000; i++) {
 		mid=(l+r)/2;
		if(check(mid)>=t) {
			l=mid;
		} else {
			r=mid;
		}
	}
	printf("%.6lf",l);
	return 0;
}
/*
3 9
3 2
6 3
9 4
*/

T5 修改数组
首先把所有 a [ i ] a[i] a[i]都减去 i i i,然后题目变成修改一些数,要求改成非负整数,且满足序列不减。
把所有 a [ i ] > = 0 a[i]>=0 a[i]>=0 a [ i ] a[i] a[i]提取出来,跑最长不降子序列。因为序列不减,所以剩下的 a [ i ] < 0 a[i]<0 a[i]<0的部分一定能改成一个非负整数,使序列不减。
最后只有这个LIS的所有元素不用修改。

#include
#include
#include
#include
#define re register int
using namespace std;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
const int Size=100005;
int n,maxn,a[Size],b[Size],tree[Size];
inline int lowbit(int x) {
	return x&(-x);
}
inline void update(int x,int v) {
	for(re i=x; i<=maxn; i+=lowbit(i)) {
		if(v>tree[i]) {
			tree[i]=v;
		}
	}
}
inline int query(int x) {
	int ans=0;
	for(re i=x; i; i-=lowbit(i)) {
		if(tree[i]>ans) {
			ans=tree[i];
		}
	}
	return ans;
}
int main() {
	n=read();
	int tot=0;
	for(re i=1; i<=n; i++) {
		a[i]=read()-i;
		if(a[i]>=0) {
			b[++tot]=a[i];
		}
	}
	for(re i=1; i<=tot; i++) {
		a[i]=b[i];
	}
	sort(a+1,a+1+tot);
	maxn=unique(a+1,a+1+tot)-(a+1);
	for(re i=1; i<=tot; i++) {
		b[i]=lower_bound(a+1,a+1+maxn,b[i])-a;
	}
	int ans=0;
	for(re i=1; i<=tot; i++) {
		int x=query(b[i]);
		update(b[i],x+1);
		if(x+1>ans) {
			ans=x+1;
		}
	}
	printf("%d",n-ans);
	return 0;
}
/*
12
3 3 6 6 1 2 6 1 1 3 6 4
*/

T6 小Biu看电影
不知道为什么珂以暴力dfs……

#include
#include
#include
#include
#define re register int
#define mod 1000000007
using namespace std;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x*f;
}
const int Size=300005;
const int INF=0x3f3f3f3f;
int n,m,cnt,head[Size];
struct Edge {
	int v,t,next;
} w[Size<<1];
void AddEdge(int u,int v,int c) {
	w[++cnt].v=v;
	w[cnt].t=c;
	w[cnt].next=head[u];
	head[u]=cnt;
}
int ans=INF,val[Size],dis[Size];
void dfs(int x,int sum) {
	if(sum>=ans)	return;
	if(sum+val[x]<ans) {
		ans=sum+val[x];
	}
	for(int i=head[x]; i; i=w[i].next) {
		int nxt=w[i].v;
		if(sum+w[i].t<dis[nxt]) {
			dis[nxt]=sum+w[i].t;
			dfs(nxt,sum+w[i].t);
		}
	}
}
int main() {
//	freopen("T6.in","r",stdin);
	n=read();
	m=read();
	for(re i=1; i<=n; i++) {
		val[i]=read();
	}
	for(re i=1; i<=m; i++) {
		int u=read();
		int v=read();
		int c=read()<<1;
		AddEdge(u,v,c);
		AddEdge(v,u,c);
	}
	for(re i=1; i<=n; i++) {
		memset(dis,0x3f,sizeof(dis));
		ans=val[i];
		dfs(i,0);
		printf("%d\n",ans);
	}
	return 0;
}

正解:
建一个虚点 s s s,从 s s s向所有 i i i连边权为 v a l [ i ] val[i] val[i]的边,跑dijkstra。
然后 s s s到每个点的距离就是从这个点开始能看到电影的最小花费(容易看出最小花费就是 s s s到一个点的最短距离,因为一开始建了边权为 v a l [ i ] val[i] val[i]的边)。

#include
#include
#include
#include
#define re register int
#define mod 1000000007
using namespace std;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x*f;
}
const int Size=100005;
const int INF=0x3f3f3f3f;
int n,m,cnt,head[Size];
struct Edge {
	int v,t,next;
} w[600005];
void AddEdge(int u,int v,int c) {
	w[++cnt].v=v;
	w[cnt].t=c;
	w[cnt].next=head[u];
	head[u]=cnt;
}
struct node {
	int x,t;
};
inline bool operator < (const node a,const node b) {
	return a.t>b.t;
}
int dis[Size];
void Dijkstra(int s) {
	memset(dis,0x3f,sizeof(dis));
	priority_queue<node> Q;
	Q.push((node){s,0});
	dis[s]=0;
	while(!Q.empty()) {
		int x=Q.top().x;
		Q.pop();
		for(re i=head[x]; i; i=w[i].next) {
			int nxt=w[i].v;
			if(dis[x]+w[i].t<dis[nxt]) {
				dis[nxt]=dis[x]+w[i].t;
				Q.push((node){nxt,dis[nxt]});
			}
		}
	}
}
int main() {
//	freopen("T6.in","r",stdin);
	n=read();
	m=read();
	int s=n+1;
	for(re i=1; i<=n; i++) {
		int x=read();
		AddEdge(s,i,x);
	}
	for(re i=1; i<=m; i++) {
		int u=read();
		int v=read();
		int c=read()<<1;
		AddEdge(u,v,c);
		AddEdge(v,u,c);
	}
	Dijkstra(s);
	for(re i=1; i<=n; i++) {
		printf("%d\n",dis[i]);
	}
	return 0;
}

你可能感兴趣的:(GDOI,Training,noip,模拟赛,多校联考,国庆集训,GDOI,Training)