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;
}