一道暴力题,猜想在某一步一定会出现0,于是怀着忐忑提交了代码,结果还真的是这样。
#include
using namespace std;
typedef long long ll;
ll judge(ll x) {
int minn = x % 10, maxn = x % 10;
ll temp = x;
temp /= 10;
while(temp) {
minn = min(int(temp % 10), minn);
maxn = max(int(temp % 10), maxn);
temp /= 10;
}
return x + minn * maxn;
}
int main() {
// freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while(t--) {
ll ans, n;
scanf("%lld %lld", &ans, &n);
for(ll i = 0; i < n - 1; i++) {
ll temp = judge(ans);
if(temp == ans) break;
ans = temp;
}
printf("%lld\n", ans);
}
return 0;
}
应该是一个贪心吧。
题意是对于一个分数为 e e e的选手,只能加入人数大于等于 e e e的组里,所以我们取每一组的最大值刚好等于其人数,这样就最大化的利用了所有的人,当然进行这一步之前必须得先排序。
#include
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
const int N = 2e5 + 10;
int a[N];
int main() {
// freopen("in.txt", "r", stdin);
IOS;
int t;
cin >> t;
// scanf("%d", &t);
while(t--) {
int n;
cin >> n;
for(int i = 0; i < n; i++)
cin >> a[i];
int sum = 0, ans = 0;
sort(a, a + n);
for(int i = 0; i < n; i++) {
sum++;
if(a[i] <= sum) {
sum = 0;
ans++;
}
}
cout << ans << "\n";
}
return 0;
}
这题是看了别人的思路才写出来的,我一开始一直在枚举 z z z边试图去找另外两条边的范围,但是一直wa。
当 x = a x = a x=a,最大的 y m a x = m i n ( i − a , c ) y_{max} = min(i - a, c) ymax=min(i−a,c),当 y = b y = b y=b,最大的 x m a x = m i n ( i − b , b ) x_{max} = min(i - b, b) xmax=min(i−b,b)
通过这个我们可以锁定任意的真正的最小的 x ∣ ∣ y x || y x∣∣y,我们 x m i n = i − y m a x x_{min} = i - y_{max} xmin=i−ymax, y m i n = i − x m a x y_{min} = i - x_{max} ymin=i−xmax
这里我们可以得到任意的一段符合条件的x,y的组合, n u m = ( x m a x − x m i n + 1 ) = ( y m a x − y m i n + 1 ) num = (x_{max} - x_{min} + 1) = (y_{max} - y_{min} + 1) num=(xmax−xmin+1)=(ymax−ymin+1)
然后 z z z的取值区间长度是 l o n g = m i n ( d − c + 1 , i − c ) long = min(d - c + 1, i - c) long=min(d−c+1,i−c)。
我们每次枚举的区间答案就是 a n s + = n u m ∗ l o n g ans += num * long ans+=num∗long
#include
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
int main() {
// freopen("in.txt", "r", stdin);
IOS;
ll a, b, c, d, ans = 0;
cin >> a >> b >> c >> d;
for(int i = max(a + b, c + 1); i <= b + c; i++) {
int x = min(i - b, b), y = min(i - a, c);
x = i - x;
ll l = min(i - c, d - c + 1);
ans += l * (y - x + 1);
}
cout << ans << "\n";
return 0;
}
一道构造题,这题应该是比较好想的,当 2 ∗ n > s 2 * n > s 2∗n>s的时候,我们至少会出现两个一,然后剩下的全是2,这里我们显然可以得到我们所需要的和为 s s s的所有二进制数,所以这个是侯一定是不可能有解的。
当 2 ∗ n < = s 2 * n <= s 2∗n<=s的时候我们如何构造,只需要取 n − 1 n - 1 n−1个一,然后最后一个数是 s − n + 1 s - n + 1 s−n+1就行了,最后的 k k k取 n n n这样就可以保证答案的有解性。
#include
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
int main() {
// freopen("in.txt", "r", stdin);
IOS;
int n, s;
cin >> n >> s;
if(2 * n > s) cout << "NO\n";
else {
cout << "YES\n";
for(int i = 0; i < n - 1; i++)
cout << "1 ";
cout << s - (n - 1) << "\n";
cout << n << "\n";
}
// int t;
// cin >> t;
// // scanf("%d", &t);
// while(t--) {
// }
return 0;
}
因为push和move这两个操作,我们比较容易发现这两个操作好像在最大值最小值两头是等价的,因此当在最大最小值中间可能存在一个最优值,使操作成本最低。
通过上面的分析我们可以大致的得到这是一个凹函数,并且存在最极小值,因此我们可以考虑三分去的到这个最小值。
#include
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int a[N], n, A, R, M;
ll f(int x) {
ll sum1 = 0, sum2 = 0;//分别记录大于当前值的数量,和小于当前值的数量。
for(int i = 1; i <= n; i++) {
if(a[i] > x) sum1 += a[i] - x;
else if(a[i] < x) sum2 += x - a[i];
}
ll ans = 0;
ll mid = min(sum2, sum1);
ans += mid * M;
sum1 -= mid, sum2 -= mid;//优先考虑从一个移到另一个上。
if(sum1) ans += sum1 * R;
if(sum2) ans += sum2 * A;
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
scanf("%d %d %d %d", &n, &A, &R, &M);
M = min(M, A + R);//这个操作可以等同是前两个操作的和,取一个最小值。
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int l = 0, r = 1e9;
while(l < r) {
int lmid = l + (r - l) / 3;
int rmid = r - (r - l) / 3;
if(f(lmid) >= f(rmid)) l = lmid + 1;
else r = rmid - 1;
}
printf("%lld\n", min(f(l), f(r)));
return 0;
}