Logo lxn 的博客

博客

标签
暂无

2023潍坊一中第二届编程挑战赛题解

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2023-11-23 08:28:57

T1 揽月湖

题目链接

本题考察基本表达式与数据类型。

  • int 4个字节 $(-2^{31},2^{31}-1)$ ,(-2,147,483,648 到-2,147,483,647)
  • long long 8个字节 $-2^{63},2^{63}-1$(-9,223,372,036,854,775,808到9,223,372,036,854,775,807)
  • unsigned long long 8个字节 $0,2^{64}$(0到18,446,744,073,709,551,616)

本题中,用不同的数据类型,得到不同的分数

  • 对于 $50\%$ 的数据,$r≤1×10^4$。 最大$3*10^8$在$int$范围内,预计得分50
  • 对于 $70\%$ 的数据,$r≤1×10^9$。 最大$3*10^{18}$在$long \space long$ 范围内,预计得分70.
  • 对于 $100\%$ 的数据,$r≤2×10^9$。最大$1.2*10^{19}$在$unsigned \space long \space long $范围内。得分100。

参考满分代码:

#include<cstdio>
#include<iostream>
using namespace std;
unsigned long long a;
int main()
{
	cin>>a;
	cout<<a*a*3;
	return 0;
 } 

T2 大写,小写?

题目链接

本题考察字符串的输入及ASCII码转换。

cin只能读入可见字符。用cin只能获得50分。

带空格的字符串读入用getline()

参考代码:

#include<iostream>
#include<string>
#define int long long
using namespace std;

string s;

signed main()
{
	getline(cin, s);
	for(int i=0; i<s.length(); i++)
	{
		if(s[i] >= 'a' && s[i] <= 'z')
		{
			cout << (char)(s[i] - ('a' - 'A'));
		}
		else if(s[i] >= 'A' && s[i] <= 'Z')
		{
			cout << (char)(s[i] - ('A' - 'a'));
		}
		else
		{
			cout << s[i];
		}
	}
	return 0;
}

T3 200天纪念

题目链接

本题考察循环的应用与基本表达式。 注意数据范围用$long \space long $

#include <bits\/stdc++.h>
using namespace std;
typedef long long ll; 
int main(){
  ll  N;
  int K;
  cin >> N >> K;
  for (int i = 0; i < K; i++) {
    if (N % 200 == 0) N \/= 200;
    else N = N * 1000 + 200;
  }
  cout << N << endl;
  return 0;
}

T4 走方格

题目链接

本题考察分类讨论。 从数据来看,光标的移动只需要向下和向右。为了简化问题,我们把对称的$(n,m)(m,n)$归为同一类处理,强制$n<m$

如何走最优呢?尽量多利用Fn? 如何最大化利用Fn?

先来看特殊数据$n=1||m==1$

1 2 3 4 5 6 7 8

0 1 2 3 3 4 4 5

可以通过打表找规律和计算得出结论

$2+\lceil\frac {(m-1-2)}{2}\rceil \space \space \space m>=4$

$=\lceil\frac {(m-1-2+4)}{2}\rceil$

$=\lceil\frac {(m+1)}{2}\rceil$

$=\lfloor\frac {m}{2}+1\rfloor$

$=\frac {m}{2}+1$

n>1的情况 已经规定n<=m

如何尽可能多的运用Fn呢?

先下右走,然后Fn直到下移结束。再按一次右移,就又可以利用Fn了。

$$ f(n,m)=\left\{ \begin{aligned} m,\space n>=m-1 \

(2+(n-2)+1+\frac{(m-n-1)}{2} \space n<m-1,n>=2 \end{aligned} \right. $$ $(2+(n-2)+1+\lceil\frac{(m-n-1)}{2}\rceil$

$=\lceil\frac{(2n+m-n-1)}{2}\rceil+1$

$=\lceil\frac{(m+n-1)}{2}\rceil+1$

$=\lfloor\frac{(m+n)}{2}\rfloor+1$

$=\frac{(m+n)}{2}+1$

参考代码:

#include<bits\/stdc++.h>
using namespace std;
typedef long long ll;

ll work() {
	ll n,m,ans=0;
	cin>>n>>m; 
	if(n>m)swap(n,m);\/\/n<=m
	if(n==1){
		if(m<4)ans=m-1;
		else ans=m\/2+1;	
	} 
	else {
		if(n>=m-1)ans=m ;\/\/n=m-1 n=m 直接下右作为一组。 
		else ans=(n+m)\/2+1;
	}
	return ans;

}
int main() {
	int t;
	cin>>t; 
	while(t--) {
		cout<<work()<<"\n";
	}
	return 0;
}

T5寻找三元组

本题考察枚举的优化。

部分分1

枚举ABC,三重循环,预计得分20.

#include<bits\/stdc++.h>

using namespace std;
#define int long long
signed main()
{
	int n, ans = 0;
	cin >> n;
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j*i<=n; j++)
		{
			for(int k=1; i*j*k<=n; k++)
			{
				if(i <= j && j <= k && i * j * k <= n)
				{
					ans++;
				}
			}
		}
	}
	cout << ans;
	return 0;
}



优化1,枚举AB,计算C

#include<bits\/stdc++.h>
using namespace std;
#define int long long
signed main()
{
	int n, ans = 0;
	cin >> n;
	for(int i=1; i<=n; i++)
	{
		for(int j=i; j*i<=n; j++)
		{
			
			ans+=max(0ll,n\/(i*j) -(j-1) ); \/\/a b,c  选出>=b的数 
		}
	}
	cout << ans;
	return 0;
}



优化2

这样发现还是超时。 因为$n<=10^{11}$,且$A \leq B \leq C,ABC \leq N$

这样A的上界还可以缩小,最大为$\sqrt[3]{N}$ b的上界为$\sqrt{\frac {N}{A}}$, 总时间复制度约为 $$\displaystyle \sum_{a=1}^{\sqrt[3]{N}}*(\sqrt{\frac {N}{A}}-A+1) $$ 经过推导,时间复杂度大约为$o(N^{\frac {2}{3}})$

参考代码:

#include<bits\/stdc++.h>
using namespace std;
#define int long long
int ans;
signed main()
{
	int n;
	cin >> n;
	for(int i=1; i*i*i<=n; i++)
	{
		for(int j=i; i*j*j<=n; j++)
		{
			ans += n \/ (i * j) - j + 1;
		}
	}
	cout << ans;
	return 0;
}



T6走方格2

本题考察搜索算法。 我们从 $(1,1)$ 开始更新每一个点。求最短距离,首选BFS,广度优先搜索。

由于我们要求走到距离其欧式距离为 $\sqrt{M}$ 的点,设我们横坐标移动的距离为$x$,纵坐标移动距离为$y$。

则我们需要寻找这个方程的所有解: $$ x^2+y^2=M$$

我们可以只枚举正整数 $x,y $其他负数的解也顺便出来了。

但是枚举 $x,y$ 加上 BFS 时间复杂度为 $O(n^4 )$,需要优化。

  • 优化方法一:可以提前预处理符号要求的变换位置,存储,后面直接使用即可。
  • 优化方法二:枚举 $x $即可找出对应的 $y $或判断无解,所以只枚举$x$ 即可。

优化后的时间复杂度 $O(n^3)$,足已通过本题。

参考代码:

#include<bits\/stdc++.h>
using namespace std;
vector<pair<int,int> > e;
int ans[410][410];
bool done[410][410];
signed main() {
	int n,m;
	cin>>n>>m;
	for(int i=-n;i<=n;i++)
		for(int j=-n;j<=n;j++)
			if(i*i+j*j==m) e.push_back({i,j});
	queue<pair<pair<int,int>,int> > q;
	q.push({{1,1},0});
	while(!q.empty()) {
		auto tmp=q.front();q.pop();
		int x=tmp.first.first,y=tmp.first.second,w=tmp.second;
		if(done[x][y]) continue;
		done[x][y]=1,ans[x][y]=w;
		for(auto d:e) {
			int tx=x+d.first,ty=y+d.second;
			if(tx<1||tx>n||ty<1||ty>n) continue;
			if(!done[tx][ty]) q.push({{tx,ty},w+1});
		}
	}
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			if(!done[i][j]) cout<<-1<<" ";
			else cout<<ans[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

T7 数字游戏

题目链接

有的同学用贪心,用最小的和最大的匹配,这是错误的。 例如

1 8 9

9 8 1

最大值并不出现在左右端点,可能出现在中间。因此,正确的方法是将$A_i$从小到大排序,$B_i$从大到小排序,依次对应,使得最大值最小。

部分分1

每次sort排序,总时间复杂度$o(n^2log ^n)$,预计得分50.

优化

注意到$A_i,B_i$的范围比较小。每次对应可以用桶排序,根据鸽巢原理,在n大的情况下,桶内数据可能是多个,可以多组数据一起对应。A桶从小到大,B桶从大到小,匹配求最大值即可。时间复杂度$O(N*200)$。预计得分100. 参考代码:

#include<bits\/stdc++.h>
using namespace std;
const int N=100009;
typedef long long ll;
int ma[209],mb[209],ta[209],tb[209];
int la,lb,ra,rb;
int n,x,y,ans;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x>>y;
		ma[x]++,mb[y]++;
		for(int i=1;i<=200;i++)ta[i]=ma[i],tb[i]=mb[i];
		int k=0,j=200,cnt=0;
		ans=0;
		while(cnt<i){\/\/共匹配i个数
			while(j>0&&!tb[j])j--;\/\/从小到大找bi
			if(j==0)break; 
			while(k<=200&&!ta[k])k++;\/\/从大到小找ai
			if(k>200)break;
			int t=min(ta[k],tb[j]);\/\/每次可以匹配t个数,加快了匹配进度
			cnt+=t;
			ans=max(ans,k+j);
			ta[k]-=t;tb[j]-=t;				
		}
		cout<<ans<<"\n";
	}	
} 

P3374 【模板】树状数组 1

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2023-11-26 17:45:04

线段树写法。tzt模板。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
struct node
{
    int l,r,w;
}a[1000001];
int aa[600000],n,m,t=1;
void build(int u,int l,int r)\/\/建立线段树!!! 
{
    if(l==r)
        a[u].w=aa[l];
    else
    {
        int mid=(l+r)\/2;
        a[u].l=++t; 
        build(a[u].l,l,mid);
        a[u].r=++t;
        build(a[u].r,mid+1,r);
        a[u].w=a[a[u].r].w+a[a[u].l].w;
    }
}
void update(int u,int l,int r,int x,int k)\/\/进行单点修改 
{
    if(l==r)
    {
        a[u].w+=k;
    }
    else
    {
        int mid=(l+r)\/2;;
        if(x<=mid)update(a[u].l,l,mid,x,k);
        else update(a[u].r,mid+1,r,x,k);
            a[u].w=a[a[u].l].w+a[a[u].r].w;
    }
} 
int query(int u,int l,int r,int ll,int rr)
{
    int ans=0;
    if(l>=ll&&r<=rr)
    {
        return ans+=a[u].w;
    }
    else
    {
        int mid=(l+r)\/2;
        if(rr>mid)ans+=query(a[u].r,mid+1,r,ll,rr);
        if(ll<=mid)ans+=query(a[u].l,l,mid,ll,rr);
        return ans;
    }
} 
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)scanf("%d",&aa[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int u,x,k;
       \/\/ cin>>u>>x>>k;
        scanf("%d%d%d",&u,&x,&k);
        if(u==1)
            update(1,1,n,x,k);
        else
            printf("%d\n",query(1,1,n,x,k));
    } 
}

P6293 [eJOI2017] 经验

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-07 15:01:49

\/*

可以发现,这个划分的过程把树分成了n条链。
如果每个点单独,那么有最小值0,要想取得最大值,要将将递增或递减的分在不同的链里。
设dp[u][0\/1]为以u为节点,当前点属于上升链还是递减链。
u由三种可能的情况:
1.u点不属于任何孩子链,每个孩子在单独的链。
2.在某个孩子连接的递减的链上
3.在某个孩子连接的递增链上
*\/ 
#include<bits\/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+9;
int n;
ll w[N],dp[N][2];
vector<int> t[N];
void dfs(int u) {
	ll sm=0;
	for(auto v:t[u]) {
		dfs(v);
		sm+=max(dp[v][0],dp[v][1]);
	}
	dp[u][0]=dp[u][1]=sm;
	\/\/把当前节点放在哪个链上呢?都试试取最优质,扣去这个v链,u在v链上产生新的价值。
	for(auto v:t[u]) {
		if(w[v]<=w[u])\/\/在递减的链上
			dp[u][0]=max(dp[u][0],sm-max(dp[v][0],dp[v][1])+w[u]-w[v]+dp[v][0]);
		if(w[v]>=w[u])\/\/在递增的链上 ,u在
			dp[u][1]=max(dp[u][1],sm-max(dp[v][0],dp[v][1])+w[v]-w[u]+dp[v][1]);
	}
	return ;
}
int main() {
	scanf("%d",&n);
	for(int i=1; i<=n; i++)
		scanf("%lld",&w[i]);
	for(int i=1; i<n; i++) {
		int x,y;
		scanf("%d%d",&x,&y);\/\/有向树
		t[x].push_back(y);
	}
	dfs(1);
	printf("%lld\n",max(dp[1][0],dp[1][1]));
	return 0;
}

CF21B Intersection

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-07 15:09:18

\/*
初中数学 二元一次方程
单个方程是否无解
两条直线平行、重合、相交三种关系的判断。
竟然评了蓝,不合理。
*\/
#include<bits\/stdc++.h>
using namespace std;
int a[4],b[4];
bool wuje(int *c){
	if(c[1]==0&&c[2]==0&&c[3]!=0) return 1;
	return 0;
} 
int main(){
	cin>>a[1]>>a[2]>>a[3];
	cin>>b[1]>>b[2]>>b[3];
	if(wuje(a)||wuje(b))cout<<0<<endl;
	else if(a[1]*b[2]!=a[2]*b[1]) cout<<1<<endl;
	else if(a[1]*b[3]!=b[1]*a[3]||a[2]*b[3]!=b[2]*a[3])cout<<0<<endl;
	else cout<<-1<<endl;
} 

CF893E Counting Arrays

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-07 15:12:26

\/*


符号问题:放偶数个负号:,可放的符号为0,2,y-2 也就是c(y,0)+c(y,2)+...c(y,y-2)\/c(y,y-1),正好2^y的一半,也就是2^(y-1)
 分解因式,先不考虑符号。 x=a1*a2*a3*...at 把t各数放入位置1...y,空着的补1.
 如果ai=aj,还要考虑分解后相同的个数,很麻烦。
 我们把x唯一分解 x=a1^p1*a2^p2*...at^pt;
 这样相当于有t种颜色不同的球,每种个数为pi,放入y个不同的盒子,允许空盒。
 每种放置方法相互独立。最后乘起来。
 
题目的核心是盒子与小球和乘法原理。
*\/
#include<bits\/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;

const int M=1e9+7,N=1e6+109;
ll t,n,m,inv[N],fc[N],pw[N],p;
ll qp(ll a,ll b) {
	ll ans=1ll;
	while(b){
		if(b&1)ans=ans*a%M;
		a=a*a%M;
		b\/=2;
	}
	return ans;
}
ll C(int x) {\/\/c(x+m-1,m-1)把x个球放入m个盒子,允许空盒。 
	return fc[x+m-1]*inv[m-1]%M*inv[x]%M;
}
int main() {
	cin>>t;
	fc[0]=inv[0]=pw[0]=1ll;
	for(int i=1; i<N; ++i)fc[i]=1ll*i*fc[i-1]%M,pw[i]=2*pw[i-1]%M;
	inv[N-1]=qp(fc[N-1],M-2);
	for(int i=N-1; i; --i)inv[i-1]=i*inv[i]%M;
	while(t--) {
		cin>>n>>m;
		p=pw[m-1];
		for(int i=2,j; i*i<=n; ++i)if(n%i==0) {
				for(j=0; n%i==0; n\/=i)++j;
				p=p*C(j)%M;
			}
		if(n>1)p=p*C(1)%M;
		printf("%lld\n",p);
	}
	return 0;
}

P8808 [蓝桥杯 2022 国 C] 斐波那契数组

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-07 15:18:04


\/*
根据费波纳的特点,到int只有47个数据。
数列的特点,确定了开头就可以确定结尾。
数列是fibon的比例数列。计算位置与比例关系即可。
根据题目的数据范围,
*\/
#include<bits\/stdc++.h>
using namespace std;
typedef long  long ll;
const int N=2e5+9;
const int inf=1e6;
int n,f[45],a[N],b[45];
int main(){
	f[0]=f[1]=1; 
	for(int i=2;i<=44;i++){
		f[i]=f[i-2]+f[i-1];

	}
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];

	}
	int m=n;
	for(int i=0;i<n;i++){
		if(a[i]%f[i]==0)b[i]=a[i]\/f[i];
		if(f[i]>inf){m=i;break;}
	}
	sort(b,b+m);
	int ans=n,len=1;
	for(int i=0;i<m;i++){
	\/\/	cout<<b[i]<<" ";
			if(b[i]==0)continue;
			if(b[i]==b[i-1])len++;
				else ans=min(ans,n-len),len=1;	
	}
	cout<<ans<<endl;
	
	
	
} 

P8810 [蓝桥杯 2022 国 C] 数组个数

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-07 15:31:25

这是一个环形问题

方法一 dp

一般的环形问题是断环为链,扩展为2倍长度。这个问题回到尾部后还有看能否和断点连接。所以我们可以直接枚举起点。当然还是扩展一倍长度,直接用%n确定点的位置。 我们设$a$数组为可以填数值的最大值,$a_i=min(b_{i-1},b_i,b_{i+1})$。

我们设$f_{ixy}$为$i$位置的数为$y$,$i-1$位置的数为$x$。 每个状态只与其前两个位置相关。 枚举起点状态的时间复杂度为$o(max(b_i)^2)$。 确定起始状态后,进行每个结点需要$o(N*max(b_i)^3)$,枚举3个结点的状态进行转移。

初始状态01位置还不能确定,从2位置开始确定1位置的。 对每个结点来说,如果前两个结点已经确定最大值,当前结点可以任意填数。 否则当前结点只能填确定的最大值。 结束后确定了n-2位置的,把环折回来再看n-1和0状态是否合法。最后仅把合法状态计入答案。

时间复杂度分析

因此总的时间复杂度为$o(N*max(b_i)^5)$.

#include<bits\/stdc++.h>
using namespace std;
const int N=2029;
typedef long long ll;
const ll mod=1e9+7;
int n,a[N],b[N],c[N];\/\/有几个连续的。
ll f[N][11][11],ans=0ll;\/\/当前状态可以与前面两个位置的状态相关。 

ll dp(int x,int y){
	\/\/01起点确定,从2开始,再回到01判断。

	memset(f,0,sizeof(f));
	f[1][x][y]=1;
	for(int i=2;i<n;i++){
		for(int j=a[i];j>=0;j--)
			for(int k=a[i-1];k>=0;k--){
				for(int l=a[i-2];l>=0;l--){
					if(max(l,k)==b[i-1])f[i][k][j]=(f[i][k][j]+f[i-1][l][k])%mod;
					else if(j==b[i-1])f[i][k][j]=(f[i][k][j]+f[i-1][l][k])%mod;
						else break;
				}		
			}
	}
	\/\/判断 n-2 n-1,x y  决定的n-1,0 
	\/\/
	ll ans=0;
	for(int i=a[n-1];i>=0;i--)
		for(int j=a[n-2];j>=0;j--){
			if(max(max(i,j),x)==b[n-1]&&max(max(i,x),y)==b[0]){
				ans=(ans+f[n-1][j][i])%mod;
			}
		}
	return ans ;
}

int main(){

	cin>>n;
	for(int i=0;i<n;i++){
		cin>>b[i];
		b[i+n]=b[i];
	}
	for(int i=0;i<n;i++){
		a[i]=min(min(b[(i+n-1)%n],b[i]),b[(i+1+n)%n]),a[i+n]=a[n];
	}
	ans=0;
	for(int i=a[0];i>=0;i--){
		for(int j=a[1];j>=0;j--){
			ll tmp=dp(i,j);
			ans=(ans+tmp)%mod;
		}
	}
	cout<<ans<<endl;
} 

方法二

根据b数值的情况,手工做几组样例我们可以发现:

当$b_i!=b_{i+1}$时候

如果$b_i>b_{i+1}$,那么我们可以唯一确定$i-1$的值为$b_i$.

如果$b_i<c_{i+1}$,那么我们可以唯一确定$i+2$的值为$b_i$.

我们设$a$数组为可以填数值的最大值,$a_i=min(b_{i-1},b_i,b_{i+1})$。 设$c$数组为数组可以确定填上的数值。

现在$c$内连续空着的数值其$a_i$是相当的。设其连续长度为$len$

  • $len<=2$,相邻数填了,方案数为$(a_i+1)^{len}$
  • $len>=3$,那每3个数至少一个填$a_i$

统计出连续长度后,我们可以设$f_{i0/1}$代表当前填数是否为$t=a_i$,然后用动态规划转移。

f[i][1]=(f[i-1][0]+f[i-1][1]);

f[i][0]=(f[i-1][1]t+f[i-2][1]t*t);

特殊情况,所有的$b_i$都相等

这时,就在一个环形区域内填数,每3个数至少有一个$b_i$,初始状态与线性稍微不同,进行特殊处理,头尾合起来时候用容斥减去不合法数据。

复杂度分析

这个算法不依赖$b_i$的大小,过程都是线性的,时间复杂度为$O(N)$。


#include<bits\/stdc++.h>
using namespace std;
const int N=2029;
typedef long long ll;
const ll mod=1e9+7;
int n,a[N],b[N],c[N],rk[N];\/\/有几个连续的。
ll f[N][2],ans=1ll;
ll work(int len,int t){\/\/环的情况:连续长度未len,最大数为t, 
	if(len==1)return 1ll;
	if(len==2)return ((t+1)*(t+1) )-1;
	for(int i=0;i<=len;i++)f[i][0]=f[i][1]=0;
	f[0][0]=t,f[0][1]=1;
	f[1][0]=(t+1)*t;
	f[1][1]=t+1;
	for(int i=2;i<len;i++){
		f[i][1]=(f[i-1][0]+f[i-1][1])%mod;
		f[i][0]=(f[i-1][1]*t+f[i-2][1]*t*t)%mod;
	} 
	return (f[len-1][0]+f[len-1][1])%mod;
} 
ll work2(int len,int t){\/\/线性情况:连续长度未len,最大数为t, 
	if(len==1)return ll(t+1);
	if(len==2)return ((t+1)*(t+1) );
	for(int i=0;i<=len;i++)f[i][0]=f[i][1]=0;
	f[0][0]=t,f[0][1]=1;
	f[1][0]=(t+1)*t;
	f[1][1]=t+1;
	for(int i=2;i<len;i++){
		f[i][1]=(f[i-1][0]+f[i-1][1])%mod;
		f[i][0]=(f[i-1][1]*t+f[i-2][1]*t*t)%mod;
	} 
	return (f[len-1][0]+f[len-1][1])%mod;
}
 
int main(){

	cin>>n;
	for(int i=0;i<n;i++)cin>>b[i],b[i+n]=b[i]; 
	for(int i=0;i<n;i++){
		a[i]=min(min(b[(i-1+n)%n],b[i]),b[(i+1)%n]);
		a[i+n]=a[i];
	}
	memset(c,-1,sizeof(c));
	int flag=-1;
	for(int i=0;i<n;i++) { \/\/先把可以确定的确定。 
		if(b[(i-1+n)%n]<b[i])c[(i+1)%n]=c[(i+1)%n+n]=b[i],flag=(i+1)%n;
		else if((b[(i-1+n)%n]>b[i]))c[(i-2+n)%n]=c[(i-2+n)%n+n]=b[(i-1+n)%n],flag=(i-2+n)%n;
	}

	\/\/找出连续的数据段。
	if(flag==-1){\/\/所有数据一样,构成环。 
		ans=work(n,a[0]);
		if(n==4)ans=(ans-(ll)pow(a[11],3))%mod;
		else if(n>4)ans= (ans-(ll)pow(a[11],4)-2*(ll)pow(a[11],3))%mod;
		if(ans<0)ans+=mod;
		cout<<ans<<endl;
		return 0;
	}
	int len=0;
	for(int i=flag;i<=n+flag;i++) {
		if(c[i%n]>-1)len=0;
		else {
			len++;
			if(c[(i+1)%n]>-1){
				ll tmp=work2(len,a[i%n]);
				ans=ans*tmp%mod;
			}
		}
		
	}
	cout<<ans<<endl;
	
} 

\/*
6
8 8 8 5 5 4
366
*\/ 

P8807 [蓝桥杯 2022 国 C] 取模

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-08 09:37:57

要求不同的$x,y$:使得:$ n\% x=n \% y$。也就是$n-c=ax=by=x*y$; 也可以表示为$n \%x=c$,是否存在不同的$x$使得$n\%x=c$成立 我们枚举$x$和$c$的所有可能性 $$ n \quad mod \quad x= \begin{cases} 0,\quad x=1\ 0,1, \quad x=2\ 0,1,2 \quad x=3\ 0,1,2...m-1 \quad x=m \end{cases} $$ 假设只有唯一的x使得上面的式子成立。 因为$n\%1=0$,所以$n\%2$只能$=1$ 同理:要只有唯一的解,方程只能为: $$

\begin{cases} n\%2=1\ n\%3=2\ n\%4=3\ ...\ n\%m=m-1 \end{cases} $$ 那这样的n有多少个呢?根据扩展中国剩余定理$n=lcm(2,3,..m)-1$有唯一解。 根据题目的数据范围,m只有再较小的情况下才可能成立,经过计算,$m<=13$

因此,只需要判断$n\%i=i-1$的情况即可。

#include<bits\/stdc++.h>
typedef long long ll;
using namespace std;
ll n,m;
bool work(){
	scanf("%lld%lld",&n,&m);
\/\/	if(m*(m-1)<n-m+2)return 0;
	if(m>13)m=13;

	for(int k=2;k<=m;k++){
		if(n%k!=k-1)return 1;
	}
	return 0;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		if(work())puts("Yes");
		else puts("No");
	}
	
} 

P8578 [CoE R5] So What Do We Do Now?

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-08 09:46:08

构造题,dfs序可以使得差值最小。



#include<bits\/stdc++.h>
using namespace std;
const int N=1e6+9;
vector<int>t[N];
int n,dfn[N],u,v;
long long ans;
void dfs(int u,int fa){
	dfn[u]=++id;
	for(auto v:t[u]){
		if(v==fa)continue;
		dfs(v,u);
	}	
}

int main(){
	memset(din,63,sizeof(din));
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		t[u].push_back(v) ;
		t[v].push_back(u);	
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)cout<<dfn[i]<<" ";
} 

CF1503C Travelling Salesman Problem

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2022-11-08 09:55:25

从1号出发,回到1号,假设为1342所走路径为13,34,42,21,移动个顺序为3421,所走路径为一样的,所以出发点无差别。

i->j花费max(ci,aj-ai),也就是max(0,aj-ai-ci)+ci

每个ci必需花费,也就是max(0,aj-ai-ci),当aj<=ai+ci时候可以花费小.如何安排可以让汉密尔顿回路权值最小?

按照a从小到大排序后,从大数走到小数时候权值一定为0。我们求从1到n的最短路,然后从n到1,将没走过的点逆序走一遍权值为0

因此转化为求从1到n的最短路。如何建图呢?

任意i>j那么从i到j到的权值为0 .i+1-i权值为0,从i到j(i<j)如何建边呢? 对每个i,找出前面最接近的ai+ci的aj连边,aj<ai+ci连0边,j+1连接对应权值的边。。 
0,0 1 :1
1,2 3:5
2,3 0:3
3,4 2:6
4,7 1:8
5,8 4:12 

0号到1号连w=1的边
1号到2号w=0
1号到3号w=1,连解更大的权值更大,不会先更新到。没必要去连接更大的边。 
#include <bits\/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+9; 

vector<pair<ll,ll> >city; 
int main() {
    int n;
    cin >> n;
    ll ans = 0;
    for(int i = 0; i < n; i++) {
        ll a, c;
        cin >> a >> c;
        city.push_back({a, c});
        ans += c;
    }
    sort(city.begin(), city.end());\/\/按照a排序 
    priority_queue<pair<ll, int> > Q;\/\/-值入 
    vector<bool>vis(n, false);
    Q.push({0, 0});
    while(!Q.empty()) {\/\/dijstrla
    	\/\/std::tie会将变量的引用整合成一个tuple,从而实现批量赋值。tuple,pair的扩展。 
        ll d; int i;
        tie(d, i) = Q.top(); Q.pop();
        if(vis[i]) continue;
        vis[i] = true;
        if(i == n - 1) {
            ans-= d;
            break;
        }
        if(i > 0) {
            Q.push({d, i - 1});
        }
        int j = lower_bound(city.begin(), city.end(), make_pair(city[i].first + city[i].second, LLONG_MAX)) - city.begin() - 1;
        Q.push({d, j});\/\/找到第一小于,连0权边。 
        if(j + 1 < n) {
            Q.push({d - city[j + 1].first + city[i].first + city[i].second, j + 1});
            \/\/ -(-d+ aj-(ai+ci)) 
        }
        
    }
    cout << ans << '\n';
}
共 90 篇博客