20250821 OI 总结

T1

错因:考试时想到的贪心是:

比较选择分段购买与整体购买求一个最大值。

这样子是不对的。

比如:

5 1
1 2 3 7 8

根据我的思路,结果应该是 \(max(m+s[n]-s[1]+1,n \times (m+1 )) = 9\),但实际结果应该是 \(7\)

总结:下次贪心记得验证其正确性

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define ull unsigned long long
#define int int
#define db double
const int MAXN = 1e5 + 7;
const int INF = 1e9 + 7;
const int MOD = 1e9 + 7;
ll n,m;
ll a[MAXN];
int main(){
	scanf("%lld%lld",&n,&m);
	for(int i = 1;i <= n;i++)
		scanf("%lld",&a[i]);
	ll ans = 1 + m;
	for(int i = 2;i <= n;i++){
		ll res1 = 1 + m;
		ll res2 = a[i] - a[i-1];
		ans += min(res1,res2);
	}
	printf("%lld\n",ans);
	return 0;
}

T2

本来可以的 100pts 的,但是一点小细节导致挂了。

错因分析:如果次数为 1 ,没有判断为 1 的系数。

总结:

1. 遇到大模拟先跳过,到时候再去做
2. 模拟劲量一次写对,不然调试可是很爽的!!!
3. 写完了一定要检查
4. 一道题浪费太多时间,可以先跳过!!
#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define ull unsigned long long
#define int int
#define db double
const int MAXN = 1e5 + 7;
const int INF = 1e9 + 7;
const int MOD = 1e9 + 7;
int n;
int a[MAXN];
int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n + 1;i++)
		scanf("%d",&a[i]);
	for(int i = 1;i <= n + 1;i++){
		if(i == 1){
			if(a[i] == 0) continue;
			if(n - i + 1 == 0){
				if(a[i] > 0)
					printf("%d",a[i]);
				else
					printf("%d",a[i]);
			}else if(n - i + 1 == 1){
				if(a[i] > 0)
					printf("%dx",a[i]);
				else
					printf("%dx",a[i]);	
			}else if(a[i] > 1){
				printf("%dx^%d",a[i],n-i+1);
			}else if(a[i] == 1){
				printf("x^%d",n-i+1);
			}else if(a[i] == -1){
				printf("-x^%d",n-i+1);
			}else{
				printf("%dx^%d",a[i],n-i+1);
			}
		}else{
			if(a[i] == 0) continue;
			if(n - i + 1 == 0){
				if(a[i] > 0)
					printf("+%d",a[i]);
				else
					printf("%d",a[i]);
			}else if(n - i + 1 == 1){
				if(a[i] == 1){
					printf("+x");
				}else if(a[i] > 0)
					printf("+%dx",a[i]);
				else
					printf("%dx",a[i]);	
			}else if(a[i] > 1){
				printf("+%dx^%d",a[i],n-i+1);
			}else if(a[i] == 1){
				printf("+x^%d",n-i+1);
			}else if(a[i] == -1){
				printf("-x^%d",n-i+1);
			}else{
				printf("%dx^%d",a[i],n-i+1);
			}
		}
	}
	return 0;
}
// 额额额,码风有亿点不好!!

T3

开始写了一个 Floyd ,后来优化成了 Dijkstra,后来就用并查集做的。(其实还有 SPFA)

思路:通过题目我们可以发现,题目中的图一定是全环有向图,因为他是一个排列,一定有一个出度和一个入度,所以他是一个全环无向图(可以把它看做无向图),因此可以用并查集。

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define ull unsigned long long
#define int int
#define db double
const int MAXN = 1000 + 7;
const int INF = 1e9 + 7;
const int MOD = 1e9 + 7;
int n,m,q;
int a[MAXN][MAXN];
int fa[MAXN];
int find(int x){
	if(fa[x] == x)
		return x;
	return fa[x] = find(fa[x]);
}
int main(){
	scanf("%d%d%d",&n,&m,&q);
	for(int i = 1;i <= n;i++)
		fa[i] = i;
	while(m--){
		for(int i = 1;i <= n;i++){
			int x;
			scanf("%d",&x);
			if(find(x) != find(i)){
				fa[x] = i;
			}
		}
	}
	while(q--){
		int x,y;
		scanf("%d%d",&x,&y);
		if(find(x) == find(y))
			puts("DA");
		else
			puts("NE");
	}
	return 0;
}
/*
6 2 2
2 1 4 5 3 6
3 2 4 1 5 6
1 5
6 3
*/

T4

思路:大模拟

错因:考场时,转的时候不知道是什么错误,一直有重复的。考试完才想到可以把它“抠”出来,再转,再搞回去。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define int int
#define db double
const int MAXN = 900 + 7;
const int INF = 1e9 + 7;
const int MOD = 1e9 + 7;
int n,m;
int a[MAXN][MAXN],c[MAXN][MAXN],idx;
int b[MAXN][MAXN];
void init(){
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= n;j++){
			a[i][j] = ++idx;
		}
	}
}
void print(){
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= n;j++){
			printf("%d ",a[i][j]);
		}
		printf("\n");
	}
}
void fanzhuan(int x,int y,int r){
	for(int i = (x-r),ii = 1;i <= (x+r);i++,ii++){
		for(int j = (y-r),jj = 1;j <= (y+r);j++,jj++){
			b[ii][jj] = a[i][j];
		}
	}
//	print();
	int len = (2*r+1);
	for(int i = 1;i <= len;i++){
		for(int j = 1;j <= len;j++){
			c[i][j] = b[j][len-i+1];
		}
	}
	for(int i = (x-r),ii = 1;i <= (x+r);i++,ii++){
		for(int j = (y-r),jj = 1;j <= (y+r);j++,jj++){
			a[i][j] = c[ii][jj];
		}
	}
//	print();
//	cout << endl;
}
int main(){
	scanf("%d%d",&n,&m);
	init();
	while(m--){
		int x,y,r,z;
//		scanf("%d%d%d%d",&x,&y,&r,&z);
		cin >> x >> y >> r >> z;
		if(z == 1){
			fanzhuan(x,y,r);
		}else{
			for(int i = 1;i <= 3;i++)
				fanzhuan(x,y,r);
		}
	} 
	print();
	return 0;
}
/*
5 4
2 2 1 0
3 3 1 1
4 4 1 0
3 3 2 1
*/

T5

思路:二分+st表

我们可以发现,元素越多, gcd 就越小,因此可以二分答案出序列中第一个小于 val 的 gcd,又可以发现,gcd 又符合可重复贡献问题,所以可以用 st 表来维护区间 gcd。

提示:

\[n+(n-1)+(n-2)+(n-3)+ \ldots + 1 = \dfrac{n(1+n)}{2} \]

\[n+(n-2)+(n-4)+ \ldots + 1 = \frac{(1+n)(\dfrac{n-1}{2}+1)}{2} \]

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define ull unsigned long long
#define int ll
#define db double
const int MAXN = 6e5 + 7;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int n;
int a[MAXN],lg[MAXN],f[20][MAXN];
int gcd(int x,int y){
  if(!y)
    return x;
  return gcd(y,x%y);
}
void init()
{
  for(int i = 1;i <= n;i++) 
    f[0][i]=a[i];
  for(int i = 2;i <= n;i++)
    lg[i] = lg[i >> 1] + 1;
  for(int j = 1;(1<<j)<=n;j++)
    for(int i = 1;i+(1<<j)-1<=n;i++)
      f[j][i]=gcd(f[j-1][i],f[j-1][i+(1<<(j-1))]);
  return; 
}

int get(int l,int r)
{
  int j = lg[r-l+1];
  return gcd(f[j][l], f[j][r+1-(1<<j)]);
}

int lower_bounds(int l,int r,int val)
{
  int i=l;
  l--,r++; 
  while(l+1<r)
  {
      int mid = l+r >> 1;
      if(get(i,mid) <= val) r = mid;
      else l = mid;
  }
  return r;
}

ll ans = 0;

signed main(){
  scanf("%lld",&n);
  for(int i = 1;i <= n;i++)
    scanf("%lld",&a[i]);
  init();
  for(int i = 1;i <= n;i++){
    int r1 = lower_bounds(i,n,1) - 1;
    int l1 = lower_bounds(i,r1,2);
    int len1 = l1 - i + 1,len2 = r1 - i + 1;
    if(len1 & 1) len1++;
    if(len2 & 1) len2--;
    if(len2 >= len1)
      ans += 1LL * ((len1+len2)*(((len2-len1)>>1)+1))>>1;
    int r2 = l1 - 1;
    int l2 = lower_bounds(i,r2,3);
    len1 = l2 - i + 1,len2 = r2 - i + 1;
    if(!(len1 & 1)) len1++;
    if(!(len2 & 1)) len2--;
    if(len2 >= len1)
      ans += 1LL * ((len1+len2)*(((len2-len1)>>1)+1))>>1;
  }
  printf("%lld\n",ans);
  return 0;
}
posted @ 2025-08-21 20:28  Ruochen_xia  阅读(2)  评论(0)    收藏  举报