1. 程式人生 > 實用技巧 >大集訓模擬賽十一

大集訓模擬賽十一

T1 解方程

題目大意

解出一元二次方程ax+by=c的一組解(x0, y0),使|x0+y0|最小。

輸入格式

共一行,三個整數a,b,c

輸出格式

共一行,為|x0+y0|的最小值。
若無解輸出“kito”。

樣例

樣例輸入

1 1 1

樣例輸出

2 3 1

演算法分析

  • 這個題求的是x + y 的最小值 我們可以直接列舉這個值
  • 設x + y == i 那麼就有 x = i - y
    然後將這個代入原方程得 a \(\times\) i - a \(\times\) y + b \(\times\) y = c
    然後移項 估參可得 (b-a) \(\times\) y = c - a \(\times\)
    i
    因此就有y = \(\frac{c-a*i}{b-a}\)
  • 同理如果x + y == -i 一樣可以求出一個y值
  • 我們知道C++中的除法是自動向下取整的 如果我們向下取整之後得到的x 與 y 仍然滿足a\(\times\)x + b\(\times\)y == c
    那麼就說明這一組解中x與y都為整數
  • 因為i是從小到大列舉的 找到符合的直接輸出就好
  • 時間複雜度嘛 比較玄學……(答案多大我就迴圈多少次 O(答案)????)

Code



#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e8+10;
typedef long long ll;

int main(){
	ll a,b,c;scanf("%lld%lld%lld",&a,&b,&c);
	if(a == b){
		ll ans = c/a;
		if(ans * a == c)printf("%lld\n",ans);
		else printf("kito\n");
		return 0;
	}
	for(register int i = 0;i <= maxn;++i){
		ll y = (c - a*i)/(b - a);
		ll x = -y + i;
		if(x * a + y * b == c){
			printf("%d\n",i);
			return 0;
		}
		y = (c + a*i)/(b - a);
		x = -y - i;
		if(x * a + y * b == c){
			printf("%d\n",i);
			return 0;
		}
	}
	printf("kito\n");
	return 0;
}

T2最佳序列(暴力造他就完了)

題目大意

  • 你得到了一個N 個非負整陣列成的序列A。
  • 我們稱由序列A 的連續若干項組成的新序列為A 的一個連續子序列。給出兩個正整數L,R(L <= R)。稱A 的每一個長度不小於L 且不大於R 的連續子序列為一個純潔序列,定義純潔度為純潔序列的所有元素的平均值。
  • 請你求出所有純潔序列中的純潔度的最大值。

輸入格式

輸入檔案的第一行包括 3 個正整數 N,L,R。
第二行包括 N 個數,按順序依次表示序列 A 的每一項。

輸出格式

輸出檔案包括一行,一個實數,保留 4 位小數,表示答案。

樣例

樣例輸入

3 2 3
6 2 8

樣例輸出

5.3333

演算法分析

  • 直接暴力掃一遍就好
  • 先列舉長度然後列舉起點 注意字首和優化一下就可以了
  • 或者可以維護一個滾動的視窗(或許是我太弱了吧 雖然複雜度一樣 但是各種計算是比較多的 會比上邊的演算法慢辣麼一丟丟就一丟丟)

Code

Code1



#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 20000+10;
ll a[maxn];
double ans;

int main(){
	int n,l,r;scanf("%d%d%d",&n,&l,&r);
	for(register int i = 1;i <= n;++i)scanf("%lld",&a[i]);
	for(register int d = l;d <= r;++d){
		ll now = 0;
		for(int i = 1;i <= d;++i)now += a[i];
		ll Max = now;
		int i = 1;
		int j = d;
		while(j <= n){
			i++,j++;
			now += a[j] - a[i-1];
			if(now > Max)Max = now;
		}
		if(1.0 * Max / d > ans)ans = 1.0 * Max / d;
	}
	printf("%.4lf\n",ans);
	return 0;
}

Code2

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 20000 + 10;
ll Max = 0;
ll a[maxn];
ll sum[maxn];
double ans;

int main(){
	int n,l,r;scanf("%d%d%d",&n,&l,&r);
	for(int i = 1;i <= n;++i)scanf("%lld",&a[i]);
	for(int i = 1;i <= n;++i)sum[i] = sum[i - 1] + a[i];
	for(int d = l;d <= r;++d){
		Max = 0;
		for(int i = 1,j;(j = i + d - 1) <= n;++i){
			if(sum[j] - sum[i-1] > Max)Max = sum[j] - sum[i-1];
		}
		if(ans < 1.0 * Max / d)ans = 1.0 * Max / d;
	}
	printf("%.4lf\n",ans);
	return 0;
}

T3週期串查詢(雜湊加線段樹 或靈性做法)

題目大意

  • 有一個串只包含數字字元。串的長度為n,下標從1開始。
  • 有兩種操作方式:
  • 1 l r c (1≤l≤r≤n, c是數字字元),表示將l到r這一段的字元全部改成c字元;
  • 2 l r d (1≤l≤r≤n, 1≤d≤r-l+1),表示詢問l到r這一段區間內的子串是否是以d為週期的串。
  • 字串s是以x (1≤x≤|s|),為週期的串的條件是:對於所有的 i從1到|s|-x, si = si + x 都成立。

輸入格式

單組測試資料。

第一行有兩個整數n, m ,k (1≤n≤10^5, 1≤m+k≤10^5),表示字串長度和修改次數和詢問次數。
第二行給出原始的串包含n位數字字元。
接下來m+k行,每行一個操作。
有兩種形式:
1 l r с (1≤l≤r≤n, c是數字字元);
2 l r d (1≤l≤r≤n, 1≤d≤r-l+1)。

輸出格式

對於每一個詢問,如果該段子串是以d為週期的,輸出YES,否則輸出NO。

樣例

樣例輸入

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

樣例輸出

NO
YES

演算法分析

  • 線段樹區間修改 區間查詢 線段樹的節點存子節點中最大值
  • 其實這個線段樹主要是優化的作用 如果要求的這個區間中 最大值與最小值相等 那麼肯定就是一樣的 最後以d為週期查詢一下就好
  • 關於瞎搞的話………… memset 與 memcpy

Code

Code1


// 比下面那種演算法慢 但是很鍛鍊碼力(自閉症患者慎入)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
char s[maxn];
int v[maxn];
int cnt;
int cur[maxn];

struct node{
	int rt,l,r,Max,Min,lazy;
}a[maxn << 2];

void build(int rt,int l,int r){
	a[rt].l = l;a[rt].r = r;
	if(a[rt].l == a[rt].r){a[rt].Max = a[rt].Min = v[l];return;}
	int mid = l + r >> 1;
	build(rt << 1,l,mid);
	build(rt << 1 | 1,mid + 1,r);
	a[rt].Max = max(a[rt << 1].Max,a[rt << 1 | 1].Max);
	a[rt].Min = min(a[rt << 1].Min,a[rt << 1 | 1].Min);
}

void updata(int rt,int k){
	a[rt].Max = a[rt].Min = k;
	a[rt].lazy = k;
}

void pushdown(int rt){
	updata(rt << 1,a[rt].lazy);
	updata(rt << 1 | 1,a[rt].lazy);
	a[rt].lazy = 0;
}

void change(int rt,int l,int r,int k){
	if(a[rt].l >= l && a[rt].r <= r){
		a[rt].Max = a[rt].Min = k;
		a[rt].lazy = k;
		return;
	}
	if(a[rt].lazy)pushdown(rt);
	int mid = a[rt].l + a[rt].r >> 1;
	if(l <= mid)change(rt << 1,l,r,k);
	if(r > mid)change(rt << 1 | 1,l,r,k);
	a[rt].Max = max(a[rt << 1].Max,a[rt << 1 | 1].Max);
	a[rt].Min = min(a[rt << 1].Min,a[rt << 1 | 1].Min);
}

int askmax(int rt,int l,int r){
	if(a[rt].l >= l && a[rt].r <= r){
		if(a[rt].lazy)pushdown(rt);
		return a[rt].Max;
	}
	if(a[rt].lazy)pushdown(rt);
	int mid = a[rt].l + a[rt].r >> 1;
	if(r <= mid)return askmax(rt << 1,l,r);
	if(l > mid)return askmax(rt << 1 | 1,l,r);
	return max(askmax(rt << 1,l,r),askmax(rt << 1 | 1,l,r));
}

int askmin(int rt,int l,int r){
	if(a[rt].l >= l && a[rt].r <= r){
		if(a[rt].lazy)pushdown(rt);
		return a[rt].Min;
	}
	if(a[rt].lazy)pushdown(rt);
	int mid = a[rt].l + a[rt].r >> 1;
	if(r <= mid)return askmin(rt << 1,l,r);
	if(l > mid)return askmin(rt << 1 | 1,l,r);
	return min(askmin(rt << 1,l,r),askmin(rt << 1 | 1,l,r));
}

void first(int rt,int l,int r,int k){
	if(a[rt].lazy)pushdown(rt);
	if(a[rt].l == a[rt].r){cur[++cnt] = a[rt].Max;return;}
	int mid = a[rt].l + a[rt].r >> 1;
	if(l <= mid)first(rt << 1,l,r,k);
	if(r > mid)first(rt << 1 | 1,l,r,k);
}

bool check(int k){
	if(k > cnt)return 0;
	for(int i = 1;i <= cnt - k;++i)if(cur[i] != cur[i + k])return 0;
	return 1;
}

int main(){
	//freopen("Data.in","r",stdin);
	//freopen("c.out","w",stdout);
	int n,m,K;scanf("%d%d%d",&n,&m,&K);
	scanf("%s",s+1);
	for(int i = 1;i <= n;++i)v[i] = s[i] - '0';
	build(1,1,n);
	for(int i = 1;i <= m + K;++i){
		int flag,l,r,k;scanf("%d%d%d%d",&flag,&l,&r,&k);
		if(flag == 1)change(1,l,r,k);
		if(flag == 2){
			cnt = 0;
			first(1,l,r,k);
			if(askmax(1,l,r) == askmin(1,l,r)){
				printf("YES\n");
				continue;
			}
			if(check(k))printf("YES\n");
			else printf("NO\n");
		}
	}
	return 0;
}

Code2

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
char s[maxn];
int a[maxn],h[maxn];

int main(){
	int n,m,k;scanf("%d%d%d",&n,&m,&k);
	scanf("%s",s+1);
	k += m;
	while(k--){
		int flag,l,r,k;scanf("%d%d%d%d",&flag,&l,&r,&k);
		if(flag == 1)memset(s + l,k + '0',r - l + 1);
		if(flag == 2 && memcmp(s + l,s + l + k,r - l - k + 1))printf("NO\n");
		else if(flag == 2)printf("YES\n");
	}
	return 0;
}

T4追捕(送分題)

題目大意

  • 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。

樣例

樣例輸入

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

樣例輸出

2/3
1 2 3 5 6 9 

演算法分析

  • 就是dfs唄 把題好好看看就懂了 能到達這個點的點就相當於反向邊這個點能到達的點
  • 把反向邊建好直接dfs就好了

Code



#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int cnt,head[maxn];
int vis[maxn];
int num;

struct node{
	int next,to;
}a[maxn];

void add(int x,int y){
	a[++cnt].to = y;
	a[cnt].next = head[x];
	head[x] = cnt;
}

int exgcd(int a,int b){return b ? exgcd(b,a % b) : a;}

void dfs(int s){
	vis[s] = 1;
	for(int i = head[s];i;i = a[i].next){
		int v = a[i].to;
		if(vis[v])continue;
		dfs(v);
		vis[v] = 1;
	}
}

int main(){
	int n,m,flag;scanf("%d%d%d",&n,&m,&flag);
	for(int i = 1;i <= m;++i){
		int x,y;scanf("%d%d",&x,&y);
		add(y,x);
	}
	int s;scanf("%d",&s);
	dfs(s);
	for(int i = 1;i <= n;++i){
		if(vis[i])num++;
	}
	int cjc = exgcd(n,num);
	printf("%d/%d\n",num/cjc,n/cjc);
	if(!flag)return 0;
	for(int i = 1;i <= n;++i){
		if(vis[i])printf("%d ",i);
	}
	return 0;
}