期末考完了补的第一场。考前打了一场,不过因为专心应考掉了30分。现在考完了做起题比之前舒服多了。这场开了vp,四十分钟出了四题。E本来想连着Hard Version一起做了,结果有点问题搞不出来。最后发现我的这个方法可以把Easy Version解决,然后才写了Easy Version。一个半小时出了五题。还应该再快一点的。
顺便以后补题都用vp了。效果似乎比干做好。
等差数列。首尾相加肯定是最优的。
#include
using namespace std;
typedef long long ll;
int main()
{
int t; cin >> t;
while (t--)
{
int n; cin >> n;
cout << (n + 1) / 2 << endl;
}
return 0;
}
题目比较难懂。
因为这个是连续填的,所以很容易计算。而且观察到任意两行之间填充的序列有周期性,所以我们只要考虑第一个填充的格子在第一行的第几列就可以了。
如果 r < n r
如果当 r ≥ n r\ge n r≥n时分两种情况考虑。当 r < n r
#include
using namespace std;
typedef long long ll;
int main()
{
int t; cin >> t;
while (t--)
{
ll n, r; cin >> n >> r;
ll ans = 0;
if (r < n) cout << (r + 1) * r / 2 << endl;
else cout << n * (n - 1) / 2 + 1 << endl;
}
return 0;
}
考虑一个简单的贪心。发现其实两种饼干的顺序对结果并没有影响,我们可以先把小的换到 a a a。
因为第二种客人一定会先拿少的,感性上肯定是先安排所有第二种客人都先拿完再安排第一种客人。这样剩下的饼干能保证全被第一种客人拿走。
两饼干相等的情况可以不要管,反正顺序对结果没有影响。先拿第一个或者第二个而导致个数不一样的话再把两个数交换一下就好了。
#include
using namespace std;
typedef long long ll;
int main()
{
int t; cin >> t;
while (t--)
{
ll a, b, n, m; cin >> a >> b >> n >> m;
if (a > b) swap(a, b);
if (a < m) cout << "No" << endl;
else
{
a -= m;
if (a + b >= n) cout << "Yes" << endl;
else cout << "No" << endl;
}
}
return 0;
}
这题怎么说呢,感觉来了一下子就能做出来。之前没做过这种类似的确实很容易卡。
之前我补过之前某一场的另一道矩阵构造题,这两题比较像,所以基本就是光速出的。之前那个是横向构造周期,这次是斜向。
那这是怎么想到的呢?考虑特殊情况: k = n k=n k=n时。这时候自己画一画就会发现对角线摆法是最优的。然后根据这个对角线可以猜测按照对角线填充应该能使这个 f f f最小。实际操作一下确实可以满足,因为这个方法每次摆的位置在同一个周期内行和列都是不同的。之后就是输出就好了。
不过之前还要求个 f f f的值。有过构造矩阵题目的做题经验想出构造法之后很容易知道 f f f只能等于 0 0 0或者 2 2 2。
一般来说感觉这种题目的答案会有特定的生成法,不需要写很长代码。按照算法一次一次填就可以了。特判很多的话十有八九就是错了。
#include
using namespace std;
typedef long long ll;
const int MAXN = 310;
int main()
{
int t; cin >> t;
while (t--)
{
int n, k; cin >> n >> k;
if (k % n == 0) cout << 0 << endl;
else cout << 2 << endl;
bool mp[MAXN][MAXN] = {0};
int tot = 0;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
if (tot == k) goto OUT_LOOP;
mp[j][(i + j) % n] = 1, tot++;
}
}
OUT_LOOP:;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
cout << mp[i][j]? 1: 0;
cout << endl;
}
}
return 0;
}
这题比较麻烦。转化一下题意,实际上让你求的这个 f ( x ) f(x) f(x)是满足以下条件的 a [ i ] a[i] a[i]的排列数:
{ a [ 1 ] ≤ x a [ 2 ] ≤ x + 1 a [ 3 ] ≤ x + 2 . . . a [ n ] ≤ x + n \begin{cases}a[1]\le x\\ a[2]\le x+1 \\ a[3]\le x+2\\...\\a[n]\le x+n\end{cases} ⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧a[1]≤xa[2]≤x+1a[3]≤x+2...a[n]≤x+n
乍一看不太会求。但如果我们定义 s u m [ i ] sum[i] sum[i]为 a [ i ] a[i] a[i]中小于等于 i i i的元素个数,那么排列数就可以用乘法原理写出来了: f ( x ) = Π i = 0 n − 1 ( s u m [ x + i ] − i ) f(x)=\Pi_{i=0}^{n-1}(sum[x+i]-i) f(x)=Πi=0n−1(sum[x+i]−i)
然后一个一个判断能不能被 p p p整除就能过Easy Version了。Hard Version没仔细想,可能结果能用数论再优化一下。
不过要注意这个 s u m sum sum要开大一点。因为每次有 n n n遍循环,所以至少要开 m a x { a [ i ] } + n max\{a[i]\}+n max{a[i]}+n的大小。
#include
using namespace std;
typedef long long ll;
const int MAXN = 5010;
const int INF = 0x3f3f3f3f;
int main()
{
int n, p; cin >> n >> p;
vector <int> a(n), sum(MAXN, 0);
int minimum = INF, maximum = -1;
for (int i = 0; i < n; ++i)
{
cin >> a[i];
sum[a[i]]++;
minimum = min(minimum, a[i]), maximum = max(maximum, a[i]);
}
for (int i = 1; i < MAXN; ++i) sum[i] += sum[i - 1];
vector <int> ans;
for (int i = minimum; i <= maximum; ++i)
{
for (int j = 0; j <= n - 1; ++j)
if ((sum[i + j] - j) % p == 0) goto OUT_LOOP;
ans.push_back(i);
OUT_LOOP:;
}
cout << ans.size() << endl;
for (auto &i : ans) cout << i << ' ';
cout << endl;
return 0;
}