比赛过程
这一场曹老师是MVP,F题急于签到还wa了一发,以后决定签到题还是得稳一点,检查一下再交,我整个人当天大比赛的时候状态并不好,E题当时想了好久没想到,但其实去年寒假集训的时候做过类似的题目应该有感觉,我当时还特意记过,只是当时没有重点去补,映像不深,D本来是能过的,奈何我们队的最长上升子序列的板子有问题,板子不对,wa到崩溃。。。。
题解
D
题意
给定一个1~n(2<=n<=500)的排列,有两种操作:
1.将倒数第二个数放到开头;
2.将第一个数放到最后;
连续的操作1(包括1次)称为一段。现在要将排列变成1~n,要使得段数尽可能少,输出这个最小值。
解法
连续的操作1可以看作把除最后一位的数的剩下序列分为两部分并交换这两部分的位置,连续操作2可以看作把整个序列分成两部分并交换这两部分的位置。画图之后不难得出先进行连续的操作2可以把任意数字移动到序列末尾,然后再用若干次连续的操作2,再用若干次连续的操作1可以实现将任意的数字插入到相对原序列的任意位置,因此只需要不断旋转序列,求其的最长上升子序列即可。
代码
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <string>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <fstream>
using namespace std;
#define ms(a, x) memset(a, x, sizeof(a))
#define fore(i, a, n) for (ll ll i = a; i < n; i++)
#define ford(i, a, n) for (ll ll i = n - 1; i >= a; i--)
#define si(a) scanf("%d", &a)
#define sl(a) scanf("%lld", &a)
#define sii(a, b) scanf("%d%d", &a, &b)
#define siii(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define sll(a, b) scanf("%lld%lld", &a, &b)
#define slll(a, b, c) scanf("%lld%lld%lld", &a, &b, &c)
#define ss(a) scanf("%s", a);
#define debug(a) cout << a << endl
#define pr(a) printf("%d ", a)
#define endl '\n'
#define pi acos(-1.0)
#define tr t[root]
#define IO ios::sync_with_stdio(false), cin.tie(0)
#define ull unsigned long long
#define py puts("Yes")
#define pn puts("No")
#define pY puts("YES")
#define pN puts("NO")
#define re(i, a, b) for (int i = a; i <= b; ++i)
#define de(i, a, b) for (int i = a; i >= b; --i)
#define all(x) (x).begin(), (x).end()
const double eps = 1e-3;
inline int sgn(const double &x) { return x < -eps ? -1 : x > eps; }
typedef long long ll;
const ll inf = 0x3f3f3f3f;
template <class T>
inline void cmin(T &a, T b) { ((a > b) && (a = b)); }
template <class T>
inline void cmax(T &a, T b) { ((a < b) && (a = b)); }
long long PRIMES[223] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409};
const int MAXN = 1e5 + 5;
const ll mode = 1e9 + 7;
//快读-----------------------------------------------------------------------------------------
namespace fastIO
{
#define BUF_SIZE 100000
#define OUT_SIZE 100000
//fread->read
bool IOerror = 0;
//inline char nc(){char ch=getchar();if(ch==-1)IOerror=1;return ch;}
inline char nc()
{
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if (p1 == pend)
{
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);
if (pend == p1)
{
IOerror = 1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
template <class T>
inline bool read(T &x)
{
bool sign = 0;
char ch = nc();
x = 0;
for (; blank(ch); ch = nc())
;
if (IOerror)
return false;
if (ch == '-')
sign = 1, ch = nc();
for (; ch >= '0' && ch <= '9'; ch = nc())
x = x * 10 + ch - '0';
if (sign)
x = -x;
return true;
}
inline bool read(double &x)
{
bool sign = 0;
char ch = nc();
x = 0;
for (; blank(ch); ch = nc())
;
if (IOerror)
return false;
if (ch == '-')
sign = 1, ch = nc();
for (; ch >= '0' && ch <= '9'; ch = nc())
x = x * 10 + ch - '0';
if (ch == '.')
{
double tmp = 1;
ch = nc();
for (; ch >= '0' && ch <= '9'; ch = nc())
tmp /= 10.0, x += tmp * (ch - '0');
}
if (sign)
x = -x;
return true;
}
inline bool read(char *s)
{
char ch = nc();
for (; blank(ch); ch = nc())
;
if (IOerror)
return false;
for (; !blank(ch) && !IOerror; ch = nc())
*s++ = ch;
*s = 0;
return true;
}
inline bool read(char &c)
{
c = nc();
if (IOerror)
{
c = -1;
return false;
}
return true;
}
template <class T, class... U>
bool read(T &h, U &... t) { return read(h) && read(t...); }
#undef OUT_SIZE
#undef BUF_SIZE
}; // namespace fastIO
using namespace fastIO;
//-----------------------------------------------------------------------------------------
ll p[MAXN], dp[MAXN];
int main()
{
int n;
read(n);
re(i, 1, n)
{
read(p[i]);
}
ll ma = 0;
re(i, 1, n)
{
re(j, 1, n)
{
dp[j] = 1;
re(k, 1, j - 1) if (p[k] < p[j])
dp[j] = max(dp[j], dp[k] + 1);
ma = max(ma, dp[j]);
}
ll tem = p[1];
re(i, 1, n - 1)
{
p[i] = p[i + 1];
}
p[n] = tem;
}
printf("%lld\n",n- ma);
}
E
题意
给定一个序列p求有多少个长度为n的序列a,能够用swap(a[i],a[p[i]])的方式排成一个有序序列。
解法
之前看到这种和对应下标的数交换位置的题是真的很怕,因为当时离散没学好,就很容易乱,但是后来在曹老师的引导下发现这个所谓的交换是由若干个循环节构成的,所以只需要求所有循环节的最小公倍数即可,然而题目还有一个坑,那就是会超过long long,幸好我方机智的曹老师及时注意到并用了大数板子才过,发现python的基本语法忘光了,要不然省力多了。
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false), cin.tie(0)
#define FOR(a,b,c) for(int a=b;a<=c;a++)
#define MAXN 9999
#define MAXSIZE 1010
#define DLEN 4
class BigNum{
private:
int a[5000]; //可以控制大数的位数
int len;
public:
BigNum(){len=1;memset(a,0,sizeof(a));} //构造函数
BigNum(const ll); //将一个int类型的变量转化成大数
BigNum(const char*); //将一个字符串类型的变量转化为大数
BigNum(const BigNum &); //拷贝构造函数
BigNum &operator=(const BigNum &); //重载赋值运算符,大数之间进行赋值运算
friend istream& operator>>(istream&,BigNum&); //重载输入运算符
friend ostream& operator<<(ostream&,BigNum&); //重载输出运算符
BigNum operator+(const BigNum &)const; //重载加法运算符,两个大数之间的相加运算
BigNum operator-(const BigNum &)const; //重载减法运算符,两个大数之间的相减运算
BigNum operator*(const BigNum &)const; //重载乘法运算符,两个大数之间的相乘运算
BigNum operator/(const int &)const; //重载除法运算符,大数对一个整数进行相除运算
BigNum operator^(const int &)const; //大数的n次方运算
int operator%(const int &)const; //大数对一个int类型的变量进行取模运算
bool operator>(const BigNum &T)const; //大数和另一个大数的大小比较
bool operator>(const int &t)const; //大数和一个int类型的变量的大小比较
void print(); //输出大数
};
BigNum::BigNum(const ll b) //将一个int类型的变量转化为大数
{
ll c,d=b;
len=0;
memset(a,0,sizeof(a));
while(d>MAXN){
c=d-(d/(MAXN+1))*(MAXN+1);
d=d/(MAXN+1);
a[len++]=c;
}
a[len++]=d;
}
BigNum::BigNum(const char *s) //将一个字符串类型的变量转化为大数
{
int t,k,index,L,i;
memset(a,0,sizeof(a));
L=strlen(s);
len=L/DLEN;
if(L%DLEN)len++;
index=0;
for(i=L-1;i>=0;i-=DLEN)
{
t=0;
k=i-DLEN+1;
if(k<0)k=0;
for(int j=k;j<=i;j++)
t=t*10+s[j]-'0';
a[index++]=t;
}
}
BigNum::BigNum(const BigNum &T):len(T.len) //拷贝构造函数
{
int i;
memset(a,0,sizeof(a));
for(i=0;i<len;i++)
a[i]=T.a[i];
}
BigNum & BigNum::operator=(const BigNum &n) //重载赋值运算符,大数之间赋值运算
{
int i;
len=n.len;
memset(a,0,sizeof(a));
for(i=0;i<len;i++)
a[i]=n.a[i];
return *this;
}
istream& operator>>(istream &in,BigNum &b)
{
char ch[MAXSIZE*4];
int i=-1;
in>>ch;
int L=strlen(ch);
int count=0,sum=0;
for(i=L-1;i>=0;)
{
sum=0;
int t=1;
for(int j=0;j<4&&i>=0;j++,i--,t*=10)
{
sum+=(ch[i]-'0')*t;
}
b.a[count]=sum;
count++;
}
b.len=count++;
return in;
}
ostream& operator<<(ostream& out,BigNum& b) //重载输出运算符
{
int i;
cout<<b.a[b.len-1];
for(i=b.len-2;i>=0;i--)
{
printf("%04d",b.a[i]);
}
return out;
}
BigNum BigNum::operator+(const BigNum &T)const //两个大数之间的相加运算
{
BigNum t(*this);
int i,big;
big=T.len>len?T.len:len;
for(i=0;i<big;i++)
{
t.a[i]+=T.a[i];
if(t.a[i]>MAXN)
{
t.a[i+1]++;
t.a[i]-=MAXN+1;
}
}
if(t.a[big]!=0)
t.len=big+1;
else t.len=big;
return t;
}
BigNum BigNum::operator-(const BigNum &T)const //两个大数之间的相减运算
{
int i,j,big;
bool flag;
BigNum t1,t2;
if(*this>T)
{
t1=*this;
t2=T;
flag=0;
}
else
{
t1=T;
t2=*this;
flag=1;
}
big=t1.len;
for(i=0;i<big;i++)
{
if(t1.a[i]<t2.a[i])
{
j=i+1;
while(t1.a[j]==0)
j++;
t1.a[j--]--;
while(j>i)
t1.a[j--]+=MAXN;
t1.a[i]+=MAXN+1-t2.a[i];
}
else t1.a[i]-=t2.a[i];
}
t1.len=big;
while(t1.a[len-1]==0 && t1.len>1)
{
t1.len--;
big--;
}
if(flag)
t1.a[big-1]=0-t1.a[big-1];
return t1;
}
BigNum BigNum::operator*(const BigNum &T)const //两个大数之间的相乘
{
BigNum ret;
int i,j,up;
int temp,temp1;
for(i=0;i<len;i++)
{
up=0;
for(j=0;j<T.len;j++)
{
temp=a[i]*T.a[j]+ret.a[i+j]+up;
if(temp>MAXN)
{
temp1=temp-temp/(MAXN+1)*(MAXN+1);
up=temp/(MAXN+1);
ret.a[i+j]=temp1;
}
else
{
up=0;
ret.a[i+j]=temp;
}
}
if(up!=0)
ret.a[i+j]=up;
}
ret.len=i+j;
while(ret.a[ret.len-1]==0 && ret.len>1)ret.len--;
return ret;
}
BigNum BigNum::operator/(const int &b)const //大数对一个整数进行相除运算
{
BigNum ret;
int i,down=0;
for(i=len-1;i>=0;i--)
{
ret.a[i]=(a[i]+down*(MAXN+1))/b;
down=a[i]+down*(MAXN+1)-ret.a[i]*b;
}
ret.len=len;
while(ret.a[ret.len-1]==0 && ret.len>1)
ret.len--;
return ret;
}
int BigNum::operator%(const int &b)const //大数对一个 int类型的变量进行取模
{
int i,d=0;
for(i=len-1;i>=0;i--)
d=((d*(MAXN+1))%b+a[i])%b;
return d;
}
BigNum BigNum::operator^(const int &n)const //大数的n次方运算
{
BigNum t,ret(1);
int i;
if(n<0)exit(-1);
if(n==0)return 1;
if(n==1)return *this;
int m=n;
while(m>1)
{
t=*this;
for(i=1;(i<<1)<=m;i<<=1)
t=t*t;
m-=i;
ret=ret*t;
if(m==1)ret=ret*(*this);
}
return ret;
}
bool BigNum::operator>(const BigNum &T)const //大数和另一个大数的大小比较
{
int ln;
if(len>T.len)return true;
else if(len==T.len)
{
ln=len-1;
while(a[ln]==T.a[ln]&&ln>=0)
ln--;
if(ln>=0 && a[ln]>T.a[ln])
return true;
else
return false;
}
else
return false;
}
bool BigNum::operator>(const int &t)const //大数和一个int类型的变量的大小比较
{
BigNum b(t);
return *this>b;
}
void BigNum::print() //输出大数
{
int i;
printf("%d",a[len-1]);
for(i=len-2;i>=0;i--)
printf("%04d",a[i]);
printf("\n");
}
ll gcd(ll a, ll b) {return b==0?a:gcd(b, a%b);}
const int maxn = 1e5 + 5;
int A[maxn];
int v[maxn] = {0};
int main(){
IO;
int n;
cin >> n;
FOR(i,1,n){
cin >> A[i];
}
BigNum ans=BigNum(1);
//ll ans = 1;
FOR(i,1,n){
if(!v[i]){
v[A[i]] = 1;
int cnt = 1;
int p = A[A[i]];
while(p!=A[i]){
v[p] = 1;
p = A[p];
cnt++;
}
ll temp = ans % cnt;
if(temp>0){
temp = gcd(cnt, temp);
ans = ans * cnt;
ans = ans / temp;
}
}
}
ans.print();
return 0;
}
F
题意
解法
画图题,没啥好说的。当时急着签到,漏了题目细节wa了一发,太莽了。
代码
vector<ll> a;
int main()
{
int n;
read(n);
ll mx = 0;
re(i, 1, n)
{
ll x;
read(x);
a.push_back(x);
mx = max(mx, x);
}
for (auto it : a)
{
ll ti = 50ll * it;
ll s = ti / mx + (ti % mx != 0);
printf("+");
re(i, 1, s)
{
printf("-");
}
printf("+\n");
printf("|");
re(i, 1, s)
{
if (i == s && it == mx)
{
printf("*");
}
else
printf(" ");
}
printf("|");
printf("%lld\n", it);
printf("+");
re(i, 1, s)
{
printf("-");
}
printf("+\n");
}
return 0;
}
I
题意
给出一个n∗mn∗m的网格,每个格子都可放H,EH,E和GG中的一个,要求HH的上下左右至少要有一个EE和一个GG,现问问n和mn和m都取无穷大时HH的个数占总格数的比值
解法
取一条斜线,在斜线上交错摆EE和GG,这样斜线的上下两条斜线都可以摆放HH,这样摆放占比最大为 2 3 \frac{2}{3} 32。比赛时候没有极限思维,老是想着在一个长方形网格里面推,长方形网格和无限大不一样,有边界,自然就推出是 1 2 \frac{1}{2} 21,卡了一会儿才意识到问题。
代码
#include<bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false), cin.tie(0)
#define endl '\n'
#define FOR(a,b,c) for(int a=b;a<=c;a++)
#define RFOR(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
typedef pair<int,int> P;
const ll mod = 1e9+7;
const int maxn = 2e5 + 5;
ll v[maxn] = {0}, prime[maxn], Cnt = 0;//v为最小质因子
int vis[maxn];
vector<P> ans;
void getpri(ll n) {for (ll i = 2; i <= n; ++i) {if (!v[i]) { v[i] = i; prime[++Cnt] = i;}for (ll j = 1; j <= Cnt; ++j) {if (prime[j] > v[i] || prime[j] * i > n) break; v[i * prime[j]] = prime[j];}}}
int main(){
IO;
getpri(maxn - 1);
int t;
cin >> t;
while(t--){
int n;
cin >> n;
FOR(i, 1, n) vis[i] = 0;
vis[1] = 1;
ans.clear();
int cnt = 0;
RFOR(i,n,2){
if(v[i]==i){
int x = i;
for (int j=n/i*i;j>i;j-=i){
if(vis[j]) continue;
if(!x) x=j;
else
cnt++, ans.push_back(P(x,j)), vis[x] = 1, vis[j] = 1, x = 0;
}
}
}
cout << cnt << endl;
for(auto i:ans){
cout << i.first << " " << i.second << endl;
}
}
return 0;
}