大假期集训模拟赛11

解方程

题目描述

解出一元二次方程 \(ax+by=c\) 的一组整数解 \((x0, y0)\) ,使 \(\left |x_0+y_0 \right |\) 最小。

输入格式

共一行,三个整数 \(a\) , \(b\) , \(c\)

输出格式

共一行,为 \(\left |x_0+y_0 \right |\) 的最小值。

若无解输出 \(“kito”\)

样例

样例输入 #1

1 1 1

样例输出 #1

1

样例输入 #2

2 3 1

样例输出 #2

0

数据范围与提示

\(30\%\) 的数据,\(a,b\) 均为质数,\(c=1\)

另有 \(20\%\) 的数据,\(a,b,c\) 均为质数。

\(100\%\) 的数据,\(a,b,c\leq 1,000,000,000\)

思路

当时考试以为是个大数论,直接跳过,拿了特判 \(10opts\) 走人。

其实思路很简单,\(x,y\) 只能是整数,推一下式子就行了。

\(\left |x_0+y_0 \right |=i\)

\(\therefore x=i-y\)\(x=-i-y\)

\(\therefore y=\frac {c\pm a*i} {b-a}\)

这样只需枚举 \(i\) ,并判断求出来的 \(y\) 是不是整数即可。

注意要特判一下 \(b==a\) ,不然会出错。

代码

#include <bits/stdc++.h>
#define int long long 
using namespace std;

const int maxn=1e9+50;
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int a,b,c;

signed main(){
	a=read(),b=read(),c=read();
	if(c==0){//加这个会更快一丢丢
		printf("0\n");
		return 0;
	}
	if(a==b){//特判a==b
		if(c%a==0){
			printf("%lld\n",c/a);
		}else{
			printf("kito\n");
		}
		return 0;
	}
	for(int i=0;i<=maxn;i++){//枚举i
		if((c-a*i)%(b-a)==0){
			printf("%lld\n",i);
			return 0;
		}
		if((c+a*i)%(b-a)==0){
			printf("%lld\n",i);
			return 0;
		}
	}
	printf("kito\n");
	return 0;
}

最佳序列

题目描述

你得到了一个 \(N\) 个非负整数组成的序列 \(A\)

我们称由序列 \(A\) 的连续若干项组成的新序列为 \(A\) 的一个连续子序列。给出两个正整数 \(L,R(L\leq R)\) 。称 \(A\) 的每一个长度不小于 \(L\) 且不大于 \(R\) 的连续子序列为一个纯洁序列,定义纯洁度为纯洁序列的所有元素的平均值。

请你求出所有纯洁序列中的纯洁度的最大值。

输入格式

输入文件的第一行包括 \(3\) 个正整数 \(N,L,R\)

第二行包括 \(N\) 个数,按顺序依次表示序列 \(A\) 的每一项。

输出格式

输出文件包括一行,一个实数,保留 \(4\) 位小数,表示答案。

样例

样例输入

3 2 3
6 2 8

样例输出

5.3333

数据范围与提示

\(20\%\) 的数据满足,\(1\leq N\leq 200\)

\(40\%\) 的数据满足,\(1\leq N\leq 2,000\)

\(100\%\) 的数据满足,\(1\leq N\leq 20,000\) , \(1\leq L\leq R\leq N\) , \(0\leq A_i\leq 1,000,000\)

思路

当时考试的时候想出了 \(3\) 种方法:

  • \(n^3\)的暴力,\(20,000\) 肯定会爆。

  • 在上面的基础上用前缀和优化一下,直接压到 \(n^2\)

  • 用单调队列来维护,二分查找答案,直接压倒 \(nlog_n\)但是我考试的时候写炸了

代码

法二暴力

#include <bits/stdc++.h>
using namespace std;

const int maxn=2e4+50;
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int n,l,r;
int a[maxn],sum[maxn];
double ans;

int main(){
	n=read(),l=read(),r=read();
	for(register int i=1;i<=n;i++){
		a[i]=read();
		sum[i]=sum[i-1]+a[i];
	}
	for(register int d=l;d<=r;d++){//register优化一下
		int now=0;
		for(register int i=1,j;(j=i+d-1)<=n;i++){
			now=max(now,sum[j]-sum[i-1]);//除法会比较慢,所以求出和的最大,再进行除法,会很快
		}
		ans=max(ans,(double)now/d);		
	}
	printf("%.4lf\n",ans);
	return 0;
}

法三(借用一手大佬的码)

#include <bits/stdc++.h>
using namespace std;

const int maxn=20000+10;

int l,r,n;
int a[maxn];
double tmp[maxn];
double sum[maxn];
int qu[maxn];
bool pd(double x){
	for(int i=1;i<=n;i++){
		tmp[i]=(double)a[i]-x;
		sum[i]=sum[i-1]+tmp[i];
	}
	int head=1,tail=0;
	int now=0;
	double ans=-1e9;
	for(int i=1;i<=n;i++){
		while(i-now>=l){
			while(head<=tail&&sum[qu[tail]]>=sum[now]){
				tail--;
			}
			qu[++tail]=now;
			now++;
		}
		while(head<=tail&&i-qu[head]>r){
			head++;
		}
		if(head<=tail){
			ans=max(ans,sum[i]-sum[qu[head]]);
		}
	}
	return ans>=0;
}
int main(){
	scanf("%d%d%d",&n,&l,&r);
	double fl=0,fr;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		fr=max(fr,(double)a[i]);
	}
	while(fr-fl>=1e-5){
		double mid=(fl+fr)/2;
		if(pd(mid)){
			fl=mid;
		}else{
			fr=mid;
		}
	}
	printf("%.4lf",(fl+fr)/2);
	return 0;
}

周期串查询

题目描述

有一个串只包含数字字符。串的长度为 \(n\) ,下标从 \(1\) 开始。

有两种操作方式:

\(1,l,r,c\) (\(1\leq l\leq r\leq n\) , \(c\) 是数字字符),表示将 \(l\)\(r\) 这一段的字符全部改成c字符;

\(2,l,r,d\) (\(1\leq l\leq r\leq n\) , \(1\leq d\leq r-l+1\)),表示询问 \(l\)\(r\) 这一段区间内的子串是否是以d为周期的串。

字符串 \(s\) 是以 \(x\) \((1\leq x\leq \left |s \right |)\) ,为周期的串的条件是:对于所有的 \(i\)\(1\)\(\left |s \right |-x\)\(s_i = s_i + x\) 都成立。

输入格式

单组测试数据。

第一行有两个整数\(n, m ,k (1\leq n\leq 10^5, 1\leq m+k\leq 10^5)\),表示字符串长度和修改次数和询问次数。

第二行给出原始的串包含 \(n\) 位数字字符。

接下来 \(m+k\) 行,每行一个操作。

有两种形式:

\(1,l,r,с\) (\(1\leq l\leq r\leq n\) , \(c\) 是数字字符);

\(2,l,r,d\) (\(1\leq l\leq r\leq n\) , \(1\leq d\leq r-l+1\))。

输出格式

对于每一个询问,如果该段子串是以 \(d\) 为周期的,输出 \(YES\) ,否则输出 \(NO\)

样例

样例输入

3 1 2
112
2 2 3 1
1 1 3 8
2 1 2 1

样例输出

NO
YES

数据范围与提示

在这个样例中第一个询问的时候子串是“ \(12\) ”,他不是以 \(1\) 为周期的,所以答案是 \(NO\) 。第二个询问的时候子串是“ \(88\) ”,是以 \(1\) 为周期的,所以答案是 \(YES\)

思路

\(hash\) 线段树的老板子了,但是 \(memset+memcmp\) 也能卡过,而且还不好卡,正解线段树反而更慢。

代码

memset+memcmp

#include <bits/stdc++.h>
using namespace std;

const int maxn=1e5+50,INF=0x3f3f3f3f;
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int n,m,k;
char a[maxn];

int main(){
	n=read(),m=read(),k=read();
	scanf("%s",a+1);
	for(int i=1;i<=m+k;i++){
		int opt=read(),l=read(),r=read(),x=read();
		if(opt==1){
			memset(a+l,x+'0',r-l+1);
		}else{
			if(memcmp(a+l,a+l+x,r-l-x+1)){
				puts("NO");
			}else{
				puts("YES");
			}
		}
	}
	return 0;
}

hash

#include <bits/stdc++.h>
#define int unsigned long long 
using namespace std;

const int maxn=2e7+50,mod=1e9+9,base=17;

inline int read(){
	int x=0,w=1;
	char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int n,m;
char s[maxn];
int a[maxn],b[maxn];
struct Node{
	int l,r,lazy;
	int hash;
}node[maxn];
void Pushup(int x){
	node[x].hash=((node[x<<1].hash*a[node[x<<1|1].r-node[x<<1|1].l+1]+node[x<<1|1].hash)%mod+mod)%mod;
}

void Pushdown(int x){
	if(node[x].lazy+1){
		node[x<<1].lazy=node[x<<1|1].lazy=node[x].lazy;
		node[x<<1].hash=b[node[x<<1].r-node[x<<1].l]*node[x].lazy%mod;
		node[x<<1|1].hash=b[node[x<<1|1].r-node[x<<1|1].l]*node[x].lazy%mod;
		node[x].lazy=-1;
	}
	return;
}

void Build(int rt,int l,int r){
	node[rt].l=l;
	node[rt].r=r;
	node[rt].lazy=-1;
	if(l==r){
		node[rt].hash=s[l]-'0'+1;
		return;
	}
	int mid=(node[rt].l+node[rt].r)>>1;
	Build(rt<<1,l,mid);
	Build(rt<<1|1,mid+1,r);
	Pushup(rt);
}

void Add(int rt,int l,int r,int w){
	if(node[rt].l==l&&node[rt].r==r){
		node[rt].lazy=w;
		node[rt].hash=b[node[rt].r-node[rt].l]*w%mod;
		return;
	}
	Pushdown(rt);
	int mid=(node[rt].l+node[rt].r)>>1;
	if(mid>=r){
		Add(rt<<1,l,r,w);
	}else if(l>mid){
		Add(rt<<1|1,l,r,w);
	}else{
		Add(rt<<1,l,mid,w);
		Add(rt<<1|1,mid+1,r,w);
	}
	Pushup(rt);
}
int Query(int rt,int l,int r){
	if(l>r)return 0;
	if(node[rt].l==l&&node[rt].r==r){
		return node[rt].hash;
	}
	Pushdown(rt);
	int mid=(node[rt].l+node[rt].r)>>1;
	if(mid>=r){
		return Query(rt<<1,l,r);
	}else if(mid<l){
		return Query(rt<<1|1,l,r);
	}else {
		return (Query(rt<<1,l,mid)*a[r-mid]+Query(rt<<1|1,mid+1,r))%mod;
	}
}

signed main(){
	n=read();m=read();
	scanf("%s",s+1);
	a[0]=1;
	b[0]=1;
	for(int i=1;i<=n;i++){
		a[i]=a[i-1]*base%mod;
		b[i]=(b[i-1]*base+1)%mod;
	}
	Build(1,1,n);
	while(m--){
		int pd=read(),l=read(),r=read(),x=read();
		if(pd==1){
			Add(1,l,r,x+1);
		}else{
			if(Query(1,l,r-x)==Query(1,l+x,r)){
				printf("YES\n");
			}else{
				printf("NO\n");
			}
		}
	}
	return 0;
}

追捕

题目描述

\(Duan2baka\) :“\(jmsyzsfq\) 天下第一蠢!”

\(jmsyzsfq\) :“你说什么?!”

于是 \(Duan2baka\) 开始了逃亡的旅程,而 \(jmsyzsfq\) 也开始追捕 \(Duan2baka\)

他们来到了一个有 \(n\) 个节点的有向图,迅速逃跑的 \(Duan2baka\) 会首先降落在有向图的某个节点T上,因为怕发出声音,他会永远静止不动。而随后跟来的 \(jmsyzsfq\) 会降落在图上随机一个节点 \(S\) 上(可以与 \(T\) 相同),并可以沿着图中的有向边随意走动。

因为 \(jmsyzsfq\) 有着敏锐的嗅觉而且绝顶聪明,他一定会沿着正确的方向寻找 \(Duan2baka\) 。你可以认为,如果图中存在一条合法的从 \(S\)\(T\) 的路径,那么 \(jmsyzsfq\) 一定会抓到 \(Duan2baka\) ——因此 \(jmsyzsfq\) 能否追捕到 \(Duan2baka\) 在他刚刚降落在图上的时候就已经确定了。

现在请你帮帮 \(jmsyzsfq\) ,在他降落前告诉他有多大的概率能够追捕到 \(Duan2baka\) ,并告诉他想要追到 \(Duan2baka\) 他可以降落在哪些节点上。

输入格式

输入第一行包含三个整数\(n\)\(m\)\(opt\) ,表示图中有 \(n\) 个节点,\(m\) 条有向边;\(opt\)\(0\)\(1\) ,含义见输出格式所述。

输入第 \(2\)\(m+1\) 行每行两个整数 \(u\)\(v\) 描述了图中一条从 \(u\)\(v\) 的有向边。

输入第 \(m+2\) 行一个整数 \(T\) 表示 \(Duan2baka\) 降落的位置。

输出格式

如果输入的 \(opt\)\(0\) ,只需要输出 \(jmsyzsfq\) 能够追捕到 \(Duan2baka\) 的概率。

如果输入的 \(opt\)\(1\) ,输出两行,第一行输出 \(jmsyzsfq\) 能够追捕到 \(Duan2baka\) 的概率;第二行按从小到大输出想要追到 \(Duan2baka\) 他可以降落的节点编号,编号间用空格隔开。

对于 \(jmsyzsfq\) 能够追捕到 \(Duan2baka\) 的概率,是一个既约分数,形如“ \(a/b\) ”(\(a,b\) 为整数),使 \(gcd(a,b)=1\) ,如果 \(a=b\) ,输出 \(1/1\)

样例

样例输入 #1

9 10 1
1 2
2 1
2 4
6 1
9 6
6 5
5 3
3 7
3 1
1 8
1

样例输出 #1

2/3
1 2 3 5 6 9 

样例 #1解释

图中共 \(9\) 个节点,能够到达节点 \(S=1\) 的节点共 \(6\) 个:\({1,2,3,5,6,9}\) 。所以能够追捕到 \(Duan2baka\) 的概率为 \(6/9=2/3\)

样例输入 #2

5 7 1
1 2
1 3
1 5
2 4
4 1
4 5
5 3
1

样例输出 #2

3/5
1 2 4

数据范围与提示

\(opt=0\)\(opt=1\) 的数据各 \(50\%\)

对于 \(opt=0\)\(opt=1\) 的情况,有着完全相同的子任务:

对于 \(10\%\) 的数据,\(n,m\leq 100\)

对于另外 \(30\%\) 的数据,\(n,m\leq 100,000\)

对于另外 \(10%\) 的数据,保证图是一个完全图,即对于任意两个点 \(u\)\(v\) ,必然有两条有向边 \(u→v\)\(v→u\)

对于另外 \(20\%\) 的数据,保证图是一个有向无环图,即对于任意一个点 \(x\) ,不存在图上一条路径从 \(x\) 经过其它点后回到 \(x\)

对于另外 \(20%\) 的数据,保证如果图上存在一条边 \(u→v\) ,一定存在一条边 \(v→u\)

对于 \(100\%\) 的数据,\(n,m\leq 1,000,000\) ,保证数据合法且不存在重边、自环。

思路

裸的 \(DFS\) ,根本没有思路可言,反向建边,反向跑一遍 \(DFS\)这不是有手就行???

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn=1e6+50,INF=0x3f3f3f3f;
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int n,m,opt,s;
int ans;
int vis[maxn];
int path[maxn];

struct Edge{
	int to,next;
}e[maxn];

int GCD(int a,int b){
	return b==0 ? a : GCD(b,a%b);
}

int tot,head[maxn];
void Add(int u,int v){
	e[++tot].to=v;
	e[tot].next=head[u];
	head[u]=tot;
}

void DFS(int u){
	vis[u]=1;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(vis[v])continue;
		vis[v]=1;
		DFS(v);
	}
}

int main(){
	n=read(),m=read(),opt=read();
	for(register int i=1;i<=m;i++){
		int u=read(),v=read();
		Add(v,u);//反向建边
	}
	s=read();
	DFS(s);//从终点往其他方向跑
	for(int i=1;i<=n;i++){
		if(vis[i]){
			path[++ans]=i;
		}	
	}
	int chu=GCD(ans,n);
	if(chu!=1){
		printf("%d/%d",ans/chu,n/chu);
	}else{
		printf("%d/%d",ans,n);
	}
	if(opt==1){
		printf("\n");
		for(register int i=1;i<=ans;i++){
			printf("%d ",path[i]);
		}
		printf("\n");
	}
	return 0;
}

不等式

题目描述

\(z\) 热衷于数学。

今天数学课的内容是解不等式: \(L\leq S\times x\leq R\)

小z心想这也太简单了,不禁陷入了深深的思考:假如已知 \(L,R,S,M\) ,满足 \(L\leq (S\times x)mod M\leq R\) 的最小正整数该怎么求呢?

输入格式

第一行包含一个整数 \(T\) ,表示数据组数,接下来是 \(T\) 行,每行为四个正整数 \(M,S,L,R\)

输出格式

对于每组数据,输出满足要求的 \(x\) 值,若不存在,输出 \(-1\)

样例

样例输入

1
5 4 2 3

样例输出

2

数据范围与提示

\(30\%\) 的数据中保证有解并且答案小于等于 \(10^6\)

另外 \(20\%\) 的数据中保证 \(L=R\)

\(100\%\) 的数据中 \(T\leq 100,M,S,L,R\leq 10^9\)

思路

首先我们将原式 \(L\leq (S\times x)mod M\leq R\) 转化成两种式子:

  • \(L\leq S\times x\leq R\)

直接求解左边的最小值,即\(x=\left \lceil \frac{L}{S} \right \rceil\),判断一下是否满足 \(S\times x\leq R\) ,否则无解。

  • \(L\leq S\times x-M\times y\leq R\)\(y\) 为新的未知数)

移项可得 \(S\times x-R\leq M\times y\leq S\times x-L\)

很明显 \(x\)\(y\) 是同增同减的,所以将 \(y\) 设为我们的主元(主要要求的未知数)。

式子可以变成:

\(-R \:\ mod \:\ S\leq M\times y \:\ mod \:\ S\leq -L \:\ mod \:\ S\)\(L\)\(R\) 会在一个 \(S\) 范围内,所以可以直接取模)

再定义:

\(L'=-R \:\ mod \:\ S,R'=-L \:\ mod \:\ S\)

\(S'=M \:\ mod \:\ S,M'=S\)

然后我们再递归求解:

  • \(L+M\times y\leq S\times x\leq R+M\times y\)

当两端点很接近时,会有:

\(x=\left \lceil \frac{L+M\times y}{S} \right \rceil=\left \lfloor \frac{R+M\times y}{S} \right \rfloor\)

所以当 \(y\) 存在时,就会存在一个解 \(x\)

所以我们每次递归的时候,将区间缩小:

原区间为 \([L,R]\) ,宽度为 \(R-L+1\)

缩小后的区间为 \([L',R']\) ,宽度为:

\(R'-L'+1=(-L\%S+S)\%S-(-R\%S+S)\%S+1=(R-L)\%S+1\leq R-L+1\)

直到区间小到只有唯一解时,并且在满足没有超出区间范围的情况下,求出唯一解。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int T;
int m,s,l,r;
int Solve(int m,int s,int l,int r){
	if(r>=m) r=m-1;//防止取模后为0
	if(l>r||m<=l) return -1;//无解的情况
	s%=m;
	int ans=(l-1)/s+1;//求出第一种情况的解
	if(ans*s<=r){//没有超出右端点
		return ans;
	}
	int a=(-r%s+s)%s;//缩小范围
	int b=(-l%s+s)%s;
	int y=Solve(s,m,a,b);//向下递归
	if(y==-1)return -1;//无解
	int x=(r+m*y)/s;//其实用左边求值,判断是否超出右边界,也是一样
	if(l<=s*x-m*y){
		return x;
	}
	return -1;//剩下都是无解
}

signed main(){
	T=read();
	while(T--){
		m=read(),s=read(),l=read(),r=read();
		printf("%lld\n",Solve(m,s,l,r));		
	}
	return 0;
}
posted @ 2020-08-01 06:24  Rubyonlу  阅读(156)  评论(0编辑  收藏  举报