2020ICPC·小米 网络选拔赛第一场 全部题解

题目类型

  • A:DP+一点点数论(几乎不用)
  • B:最短路+计算几何(判断线段相交)
  • C:签到题
  • D:图论(tarjan求连通块)
  • E:数据结构(线段树)
  • F:二分答案
  • G:看着像图论,其实构造就行了
  • H:数学推导
  • I:搜索(标程好像是bfs,但我是用dfsA的)
  • J:前缀和+差分
  • K:数学

A-Intelligent Warehouse

思路:
给定一组数,选出最多的数,保证其中任意两个数有 a i a_{i} ai a j a_{j} aj的因子或 a j a_{j} aj a i a_{i} ai的因子。
称选出的最多的数为数群(暂且这么叫,因为其中可以有相同元素,所以不能称作数集)。
考虑DP,对一个数 a i a_{i} ai d p [ a i ] dp[a_{i}] dp[ai]为满足条件且最大数为 a i a_{i} ai的数群,那么对这个数群中 a i a_{i} ai最大的因子 a j a_{j} aj,必满足数群中 a i a_{i} ai其他的因子也是 a j a_{j} aj的因子,就有 d p [ a i ] = m a x ( d p [ a j ] , d p [ a i ] ) dp[a_{i}]=max(dp[a_{j}],dp[a_{i}]) dp[ai]=max(dp[aj],dp[ai]),然后直接暴力即可。
注意:
选择的数可以有重复,所以要记得计数,比赛时我就是当成数集导致WA了好几次,这个做法刚好没TLE,官方似乎有更快的做法。

#include<bits/stdc++.h>
using namespace std;
const int N=1e7;
int cnt[N+5],dp[N+5];
int main(){
    
    
	int n,num,ans=0;
	cin>>n;
	for(int i=1;i<=n;i++){
    
    
		scanf("%d",&num);
		cnt[num]++; 
	}
	for(int i=1;i<=N;i++)
		if(cnt[i]){
    
    
			dp[i]+=cnt[i];
			for(int j=i*2;j<=N;j+=i){
    
    
				dp[j]=max(dp[i],dp[j]);
			}
			ans=max(ans,dp[i]);
		}
	cout<<ans;
	return 0;
}

B-Intelligent Robot

思路:
注意两点之间直线最短,所以最优路线永远是走线段端点,不需要在线段上走,那么只需要把所有端点(包括起点终点)连接起来,删去相交且交点不在端点的线段,就可以变成最短路问题,然后用dijkstra算法求解
判断线段相交先跨立试验后叉乘,但这题可以直接用叉乘(想一想为什么?)
注意:
这里线段相交,但交点是两线段其中之一的端点的线段不用删去。

#include<bits/stdc++.h>
using namespace std;
const int INF=1e18,N=1e6;
struct edge{
    
    
	int x1,y1,x2,y2;
}e[N];
struct point{
    
    
	int x,y;
}p[N];
point operator-(point a,point b){
    
    return (point){
    
    a.x-b.x,a.y-b.y};}
int operator *(const point &a,const point &b)
{
    
    
    int t=a.x*b.y-b.x*a.y;
    return t<0?-1:(t>0);
}
struct heap{
    
    
	double d;
	int u;
	bool operator<(const heap& temp) const{
    
    
		return d>temp.d;
	}
};
priority_queue<heap>Q;
bool line(point a1,point a2,point b1,point b2){
    
    
	return((a2-a1)*(b1-a1))*((a2-a1)*(b2-a1))<0&&((b2-b1)*(a1-b1))*((b2-b1)*(a2-b1))<0;
}
int n,m,k,first[N],qnext[N],v[N],esum=0;
double w[N],d[N];
bool vis[N];
void add(int a,int b,double c){
    
    
	v[esum]=b;
	w[esum]=c;
	qnext[esum]=first[a];
	first[a]=esum++;
}
int main(){
    
    
	memset(first,-1,sizeof(first));
	cin>>n>>m>>k;
	for(int i=1;i<=k;i++){
    
    
		cin>>e[i].x1>>e[i].y1>>e[i].x2>>e[i].y2;
		p[i*2-1].x=e[i].x1,p[i*2-1].y=e[i].y1;
		p[i*2].x=e[i].x2,p[i*2].y=e[i].y2;
	}
	cin>>p[0].x>>p[0].y>>p[2*k+1].x>>p[2*k+1].y;
	bool flag;
	for(int i=0;i<2*k+1;i++)
		for(int j=i+1;j<=2*k+1;j++){
    
    
			flag=1;
			for(int q=1;q<=k;q++)
				if(line(p[i],p[j],(point){
    
    e[q].x1,e[q].y1},(point){
    
    e[q].x2,e[q].y2})){
    
    
					flag=0;
					break;
				}
			if(flag){
    
    
				double dis=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
				v[esum]=j;
				w[esum]=dis;
				qnext[esum]=first[i];
				first[i]=esum++;
				v[esum]=i;
				w[esum]=dis;
				qnext[esum]=first[j];
				first[j]=esum++;
			}
		}
	for(int i=1;i<=2*k+1;i++) d[i]=INF;
	Q.push((heap){
    
    0,0});
	while(!Q.empty()){
    
    
		heap x=Q.top();
		Q.pop();
		int u=x.u;
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=first[u];~i;i=qnext[i])
			if(d[v[i]]>d[u]+w[i]){
    
    
				d[v[i]]=d[u]+w[i];
				Q.push((heap){
    
    d[v[i]],v[i]});
			}
	}
	printf("%.4f",d[k*2+1]);
	return 0; 
} 

C-Smart Browser

签到题,队友写的,略

D-Router Mesh

思路:
题目很显然是求连通块,用tarjan算法即可
注意:
开始给的图可能不是连通的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
const int N=3e5+5;
int fst[N],nxt[N*2],to[N*2],dfn[N],low[N],ans[N];
int n,m,ti=0,ecnt=0,sum=0,r;
bool vis[N],cut[N];
void add(int x,int y){
    
    
	nxt[++ecnt]=fst[x];
	fst[x]=ecnt;
	to[ecnt]=y;
}
void tarjan(int x){
    
    
	dfn[x]=low[x]=++ti;
	int c=0,cnt=0;
	for(int i=fst[x];~i;i=nxt[i])
		if(!dfn[to[i]]){
    
    
			tarjan(to[i]);
			low[x]=min(low[x],low[to[i]]);
			if(low[to[i]]>=dfn[x]){
    
    
				c++;
				cnt++;
				if(x!=r||c>1) cut[x]=1;
			}
		}
		else low[x]=min(low[x],dfn[to[i]]);
	if(x!=r) cnt++;
	ans[x]=cnt;
} 
int main(){
    
    
	cin>>n>>m;
	memset(fst,-1,sizeof(fst));
	memset(nxt,-1,sizeof(nxt));
//	rep(i,1,n) fa[i]=i;
	int u,v;
	rep(i,1,m){
    
    
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	rep(i,1,n)
		if(!dfn[i]){
    
    
			sum++;
			tarjan(r=i);
		}
	rep(i,1,n)
		cout<<ans[i]+sum-1<<" ";
	return 0;
}

E-Phone Network

思路:
R i , j R_{i,j} Ri,j是以j为左端点,包含1~i所以数字的序列的最小右端点,考虑 R i + 1 , j R_{i+1,j} Ri+1,j,令所以i+1的下标为 p 1 , p 2 , . . . , p s p_{1},p_{2},...,p_{s} p1,p2,...,ps,特殊的, p 0 = 0 p_{0}=0 p0=0,那么对于 ( p k − 1 , p k ] (p_{k-1},p_{k}] (pk1,pk]中的j, R i + 1 , j = m a x ( R i , j , p k ) R_{i+1,j}=max(R_{i,j},p_{k}) Ri+1,j=max(Ri,j,pk),而 ( p s , n ] (p_{s},n] (ps,n]中的j, R i + 1 , j R_{i+1,j} Ri+1,j可记作正无穷。
又由 R i , j R_{i,j} Ri,j是单调的,所以只需在 ( p k − 1 , p k ] (p_{k-1},p_{k}] (pk1,pk]中找 R i , j < p k R_{i,j}<p_{k} Ri,j<pk的那段j更新值,而 R i , j − j + 1 R_{i,j}-j+1 Ri,jj+1的最小值即答案。
注意以上操作,有区间赋值和记录区间最小值,因此考虑用线段树处理。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
#define pb push_back
const int INF=0x3f3f3f3f,N=2e5+5;
struct tr{
    
    
	int l,r,laz,p,d;
}t[N*4];
void down(int x){
    
    
	if(!t[x].laz) return;
	t[x<<1].laz=t[x<<1|1].laz=t[x<<1].p=t[x<<1|1].p=t[x].laz;
	t[x<<1].d=t[x].laz-t[x<<1].r+1;
	t[x<<1|1].d=t[x].laz-t[x<<1|1].r+1;
	t[x].laz=0;
}
void build(int x,int l,int r){
    
    
	t[x].l=l,t[x].r=r,t[x].d=INF;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
}
void chag(int x,int l,int r,int c){
    
    
	if(l>r||t[x].r<l||t[x].l>r) return;
	if(l<=t[x].l&&t[x].r<=r){
    
    
		t[x].p=t[x].laz=c;
		t[x].d=c-t[x].r+1;
		return;
	}
	down(x);
	chag(x<<1,l,r,c);
	chag(x<<1|1,l,r,c);
	t[x].p=min(t[x<<1].p,t[x<<1|1].p);
	t[x].d=min(t[x<<1].d,t[x<<1|1].d);
}
int query(int x,int l,int r,int c){
    
    
	if(l>t[x].r||t[x].l>r||t[x].p>=c) return -1;
	if(t[x].l==t[x].r) return t[x].l;
	int pos;
	pos=query(x<<1|1,l,r,c);
	if(pos==-1) pos=query(x<<1,l,r,c);
	return pos; 
}
int n,m;
vector<int>R[N];
int main(){
    
    
	cin>>n>>m;
	int temp;
	build(1,1,n);
	rep(i,1,n){
    
    
		cin>>temp;
		R[temp].pb(i);
	}
	rep(i,1,m){
    
    
		int np=0,len=R[i].size(),rr;
		rep(j,0,len-1){
    
    
			rr=query(1,np+1,R[i][j],R[i][j]);
			chag(1,np+1,rr,R[i][j]);
			np=R[i][j];
		}
		chag(1,np+1,n,INF);
		cout<<t[1].d<<" ";
	}
	return 0;
}

F-Design Problemset

思路:
简单的二分答案,但是中间数据会爆long long,而我又懒得写高精度了,于是找了一个__int128的模板。
注意:
可能会有 Σ l i > R Σl_{i}>R Σli>R Σ r i < L Σr_{i}<L Σri<L的情况,直接输出0。

#include<bits/stdc++.h>
using namespace std;
typedef __int128 ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
const int K=1e5+5;
ll a[K],l[K],r[K],k,L,R,sl=0,sr=0,x=0,y=0,ans;
__int128 read(){
    
    
    __int128 x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')f=-1;
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}

void print(__int128 x){
    
    
    if(x<0) putchar('-'),x=-x;
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
bool check(ll m){
    
    
	ll temp=m*(L-sl),cnt=0;
	rep(i,1,k)
		if(a[i]<m*l[i]) return 0;
	rep(i,1,k)
		cnt+=min(a[i]-l[i]*m,(r[i]-l[i])*m);
	return cnt>=temp;
}
int main(){
    
    
	k=read(),L=read(),R=read();
	rep(i,1,k){
    
    
		a[i]=read();
		y+=a[i];
	}
	rep(i,1,k){
    
    
		l[i]=read(),r[i]=read();
		sl+=l[i];
		sr+=r[i];
	}
	if(sl>R||sr<L){
    
    
		cout<<0;
		return 0;
	}
	while(x<=y){
    
    
		ll m=(x+y)>>1;
		if(check(m)) x=m+1,ans=m;
		else y=m-1;
	}
	print(ans);
	return 0;
}

G-Tree Projection

思路:
题目要求一个满足dfs序一个满足topo序,其实都满足topo序即可(想一想为什么?)。而考虑一下topo序的关于边的性质,不难想到,对序列中除了第一个数的任意的一个数 a i a_{i} ai,有且仅有一个 [ 1 , i − 1 ] [1,i-1] [1,i1]中的数与其连边。
topo序是一个节点的祖先必定排在它的前面,那么倘若不与排在前面的数连边,这个节点就是根节点,而与假如前面的两个数连边,考虑 a i a_{i} ai a j a_{j} aj, a i a_{i} ai a k a_{k} ak建边,根节点又是 a j a_{j} aj a k a_{k} ak的祖先,那么根节点, a i , a j , a k a_{i},a_{j},a_{k} ai,aj,ak四个节点就形成了环,不满足topo序的要求。
而题目只要求输出一种可能,那么就可以考虑这题是构造题。
利用上述的性质,这时候考虑B中每一个B[i],找到B[1~i-1]中在A中下标最小的连边。对A中任意A[i],假设其与A[i-p],A[i-q]相连,A[i-p],A[i-q],A[i]分别对应B[r],B[s],B[t],那么r,s一定在t之前,否则例如r>t>s,那么A[i-q],A[i]均与A[i-p]连边,不会产生假设的情况,而r,s在t之前,按照上述的构造方法,t只会与其中一个连边,也不会产生假设的情况。
因此按照上述的构造方法,本题是一定有解的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
#define push_back pb;
const int N=2e5+5;
int a[N],b,p[N],cnt=0,u[N],v[N];
int main(){
    
    
	int n;
	cin>>n;
	rep(i,1,n){
    
    
		cin>>a[i];
		p[a[i]]=i;
	}
	cin>>b;
	int pos=p[b],q=b;
	cout<<"YES"<<endl;
	rep(i,2,n){
    
    
		cin>>b;
		u[++cnt]=q,v[cnt]=b;
		if(p[b]<pos) pos=p[b],q=b;
	}
	rep(i,1,cnt)
		cout<<u[i]<<" "<<v[i]<<endl;
	return 0;
} 

H-Grouping

我写了最久的题目终于到了
思路:
这是一道推导式子的题目,我和官方题解的推导方法有些差别。
我得到的最终式子是
n − 1 n 2 ( 2 n − 1 ) [ ( 2 n − 1 ) ∑ i = 1 2 n a i 2 − 2 ∑ i = 1 2 n a i ( s 2 n − s i ) ] − 1 n 2 ( 2 n − 1 ) ( 2 n − 3 ) ( [ ∑ i = 1 2 n ( 2 n − i ) a i − ( s 2 n − s i ) ] 2 − ∑ i = 1 2 n [ 2 s i − s 2 n + 2 ( n − i ) a i 2 ] ) \frac{n-1}{n^2(2n-1)}\quad[(2n-1)\sum_{i=1}^{2n}a_{i}^2-2\sum_{i=1}^{2n}a_{i}(s_{2n}-s_{i})]-\frac{1}{n^2(2n-1)(2n-3)}([\sum_{i=1}^{2n}(2n-i)a_{i}-(s_{2n}-s_{i})]^2-\sum_{i=1}^{2n}[2s_{i}-s_{2n}+2(n-i)a_{i}^2]) n2(2n1)n1[(2n1)i=12nai22i=12nai(s2nsi)]n2(2n1)(2n3)1([i=12n(2ni)ai(s2nsi)]2i=12n[2sis2n+2(ni)ai2])
由于草稿纸上太多推错的式子,我也不能确定是不是这个。
推导过程实在太难打了,有兴趣的可以找我。
注意:
到处都取模,能%的都%一下,莫名其妙就过了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
const int N=1e5+5,MOD=998244353;
ll n,a[N*2],s[N*2],s2[N*2];
ll f(ll x){
    
    
	while(x<0) x+=MOD;
	return x;
}
ll sq(ll x){
    
    
	return (f(x)%MOD*f(x)%MOD)%MOD; 
}
ll qpow(int a,int x){
    
    
	ll ans=1;
	while(x){
    
    
		if(x&1) ans=ans*a%MOD;
		a=sq(a);
		x>>=1;
	}
	return ans;
}
int main(){
    
    
	cin>>n;
	int n2=n*2;
	rep(i,1,n2) cin>>a[i];
	sort(a+1,a+n2+1);
	rep(i,1,n2) s[i]=(s[i-1]+a[i])%MOD;
	ll f1=0,f2=0,f3=0,f4=0,f5=0;
	rep(i,1,n2){
    
    
		ll tmp=(i*a[i]-s[i]-(2*n-i)*a[i]+(s[n2]-s[i]))%MOD;
		f1=(f1+tmp*tmp)%MOD;
		f2=(f2+a[i]*a[i]%MOD)%MOD;
		f3=(f3+MOD+(-2*n-1+2*i)*a[i]%MOD)%MOD;
	}
	f4=(n2*f2+MOD-s[n2]*s[n2]%MOD)%MOD;
	f5=(f3*f3%MOD+MOD-f1+f4)%MOD;
	ll ans,nn=sq(n)*(n2-1)%MOD;
	ans=(f(f4*(n-1)%MOD-f5*qpow((n2-3)%MOD,MOD-2))%MOD)*qpow(nn,MOD-2)%MOD;
	cout<<f(ans)%MOD;
	return 0;
}

I-Walking Machine

思路:
大部分的解法好像都是bfs,但我比赛时是用dfs写的,这题写的比较乱也比较无脑。
注意:
记得考虑n=m=1时候,如果是考虑四角的话会把这个点重复算四遍。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5,M=1e3+5;
int n,m,ans=0;
char a[N][N];
bool vis[N][N];
void dfs(int x,int y){
    
    
	if(!vis[x][y]) vis[x][y]=1;
	else return;
	ans++;
	if(x+1<=n&&a[x+1][y]=='W')
		dfs(x+1,y);
	if(x-1>0&&a[x-1][y]=='S')
		dfs(x-1,y);
	if(y+1<=m&&a[x][y+1]=='A')
		dfs(x,y+1);
	if(y-1>0&&a[x][y-1]=='D')
		dfs(x,y-1);
}
int main(){
    
    
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];
	if(a[1][1]=='W'||a[1][1]=='A')
		dfs(1,1);
	if(a[1][m]=='W'||a[1][m]=='D')
		dfs(1,m);
	if(a[n][1]=='S'||a[n][1]=='A')
		dfs(n,1);
	if(a[n][m]=='S'||a[n][m]=='D')
		dfs(n,m);
	for(int i=2;i<m;i++){
    
    
		if(a[1][i]=='W')
			dfs(1,i);
		if(a[n][i]=='S')
			dfs(n,i);
	}
	for(int i=2;i<n;i++){
    
    
		if(a[i][1]=='A')
			dfs(i,1);
		if(a[i][m]=='D')
			dfs(i,m);
	}
	cout<<ans;
	return 0;
}

J-Matrix Subtraction

思路:
由于是比赛时候做的所以只记得是用二维前缀和及差分。
考虑到用这个了应该就很容易写出来了

#include<bits/stdc++.h>
using namespace std;
int t,n,m,a,b,c[1005][1005],p[1005][1005];
bool f(int x,int y){
    
    
	int s1=c[x-1][y]+c[x][y-1]-c[x-1][y-1],s2=0;
	if(x>=a) s2+=c[x-a][y];
	if(y>=b) s2+=c[x][y-b];
	if(x>=a&&y>=b) s2-=c[x-a][y-b];
	if(s1-s2>p[x][y]) return 0;
	else if(s1-s2<p[x][y]){
    
    
		if(x>n-a+1||y>m-b+1) return 0;
		c[x][y]=s1+p[x][y]-(s1-s2); 
	}
	else c[x][y]=s1;
	return 1;
}
void op(){
    
    
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(!f(i,j)){
    
    
				cout<<"QAQ"<<endl;
				return;
			}
	cout<<"^_^"<<endl;
}
int main(){
    
    
	cin>>t;
	while(t--){
    
    
		cin>>n>>m>>a>>b;
		memset(p,0,sizeof(p));
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				cin>>p[i][j];
		op();
	}
	return 0;
}

K-Sqrt Approaching

终于到了数学类题目
思路:
将题目进行简单的转化,就变成要找C,D满足 C D \frac{C}{D} DC n \sqrt{n} n A B \frac{A}{B} BA之间,而题目又是只需要输出一组解,所以尝试构造解。
由于我的解法和官方解法不同,所以其实样例的输出也和我的输出不同,官方解法直接给出构造,未免有点无中生有的感觉,我尝试给出得到构造的思路。
首先观察一下A,B,C,D,n的数据范围,不难猜想构造出的C,D应该可能含有nA,nB,A,B,n和常数项。而 A B \frac{A}{B} BA是有理数, n \sqrt{n} n 是无理数,如果构造的 C D \frac{C}{D} DC在他们之间,那么一直重复这个构造方法,就会越来越逼近 n \sqrt{n} n ,即找到一种迭代方法,不断逼近它。
其实这里我考虑过牛顿迭代法,但是构造出的A有二次项,于是换一种思路。
t = A B t=\frac{A}{B} t=BA,那么要做的其实是找到一个递推式形如 x n + 1 = ( p 1 n + p 2 ) x n + p 3 n + p 4 ( q 1 n + q 2 ) x n + q 3 n + q 4 x_{n+1}=\frac{(p_{1}n+p_{2})x_{n}+p_{3}n+p_{4}}{(q_{1}n+q_{2})x_{n}+q_{3}n+q_{4}} xn+1=(q1n+q2)xn+q3n+q4(p1n+p2)xn+p3n+p4的数列,其极限是 n \sqrt{n} n ,其中p,q都是整数,这时候分子分母就是C,D了。
那么这时我们对两边取极限(不考虑严谨性,就先假设存在了),即
n = ( p 1 n + p 2 ) n + p 3 n + p 4 ( q 1 n + q 2 ) n + q 3 n + q 4 \sqrt{n}=\frac{(p_{1}n+p_{2})\sqrt{n}+p_{3}n+p_{4}}{(q_{1}n+q_{2})\sqrt{n}+q_{3}n+q_{4}} n =(q1n+q2)n +q3n+q4(p1n+p2)n +p3n+p4,整理一下可以得到
q 1 n 2 + ( q 3 − p 1 ) n n + ( q 2 − p 3 ) n + ( q 4 − p 2 ) n − p 4 = 0 q_{1}n^2+(q_{3}-p_{1})n\sqrt{n}+(q_{2}-p_{3})n+(q_{4}-p_{2})\sqrt{n}-p_{4}=0 q1n2+(q3p1)nn +(q2p3)n+(q4p2)n p4=0
若该式子恒成立,那么必有 q 1 = p 4 = 0 , q 3 = p 1 , q 2 = p 3 , q 4 = p 2 q_{1}=p_{4}=0,q_{3}=p_{1},q_{2}=p_{3},q_{4}=p_{2} q1=p4=0,q3=p1,q2=p3,q4=p2
q 2 , p 3 q_{2},p_{3} q2,p3首先保证不为0,否则式子将与t无关,那么为了简化式子,不妨考虑下 q 4 = p 2 = 0 q_{4}=p_{2}=0 q4=p2=0,代回原式得到 x n + 1 = p 1 n x n + p 3 n p 3 x n + p 1 n x_{n+1}=\frac{p_{1}nx_{n}+p_{3}n}{p_{3}x_{n}+p_{1}n} xn+1=p3xn+p1np1nxn+p3n
那么就初步构造出 C D = p 1 n t + p 3 n p 3 t + p 1 n \frac{C}{D}=\frac{p_{1}nt+p_{3}n}{p_{3}t+p_{1}n} DC=p3t+p1np1nt+p3n,不妨设 t = A B > n t=\frac{A}{B}>\sqrt{n} t=BA>n ,那么就有 t = A B > C D > n t=\frac{A}{B}>\frac{C}{D}>\sqrt{n} t=BA>DC>n ,先考虑 C D > n \frac{C}{D}>\sqrt{n} DC>n ,可以得到 p 1 n − ( p 1 t + p 3 ) n + p 3 t < 0 p_{1}n-(p_{1}t+p_{3})\sqrt{n}+p_{3}t<0 p1n(p1t+p3)n +p3t<0,即 t ( p 1 n − p 3 ) > n ( p 1 n − p 3 ) t(p_{1}\sqrt{n}-p_{3})>\sqrt{n}(p_{1}\sqrt{n}-p_{3}) t(p1n p3)>n (p1n p3),那么只需要 p 1 n − p 3 > 0 p_{1}\sqrt{n}-p_{3}>0 p1n p3>0恒成立即 n > p 1 p 3 \sqrt{n}>\frac{p_{1}}{p_{3}} n >p3p1
这时候考虑最简单的形式即 p 1 = p 3 = 1 p_{1}=p_{3}=1 p1=p3=1,带入发现满足题意,所以就可以得到了构造C=n(A+B),D=A+nB,然后就是一些高精度计算了。
我还考虑过 q 3 = p 1 = 0 q_{3}=p_{1}=0 q3=p1=0,但是并不可行。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
const int L=1e5+20;
ll a[L],b[L],na[L],nb[L],c[L],d[L],n;
void read(ll p[],string q){
    
    
	p[0]=q.size();
	per(i,(p[0]-1),0)
		p[p[0]-i]=q[i]-'0';
}
void muln(ll p[],ll q[]){
    
    
	rep(i,1,p[0])
		q[i]=n*p[i];
	int temp,k;
	rep(i,1,p[0]){
    
    
		temp=q[i]/10,k=1,q[i]%=10;
		while(temp)
			q[i+k]+=temp%10,temp/=10,k++;
	}
	q[0]=p[0]+k-1;
}
void add(ll p[],ll q[],ll r[]){
    
    
	r[0]=max(p[0],q[0]);
	rep(i,1,r[0])
		r[i]+=p[i]+q[i],r[i+1]+=r[i]/10,r[i]%=10;
	if(r[r[0]+1]) r[0]++;
}
int main(){
    
    
	string A,B;
	cin>>A>>B>>n;
	read(a,A),read(b,B);
	muln(a,na),muln(b,nb);
	add(na,nb,c);
	add(a,nb,d);
	per(i,c[0],1) cout<<c[i];
	cout<<endl;
	per(i,d[0],1) cout<<d[i];
	return 0;
} 

以及附上我看见别人这题的python代码…

A=input()
B=input()
n=input()
print(A*n+B*n)
print(A+B*n)

猜你喜欢

转载自blog.csdn.net/u013455437/article/details/109860147