9.14 校内模拟赛 题解报告
9.14 校内模拟赛 题解报告
扯
T1 读入比题都难
T2 sb 搜索
T3 不知道为什么对的贪心
大概这场考试就是这样...
关于考试过程以及一些题外话
首先模一下陈绿神仙 不到半个小时把 T3 干掉了 太强了
T1 看读入就好像不是人做的亚子 读了一下题 稍微想了一下感觉好像有五十分就跑路了
T2 看着数据范围挺折半的 应该能做 虽然最后并没有过
T3 读完题大概知道要么贪心 要么 dp
然后先回去把 T2 的折半写了 又写了个爆搜 然后挂上开始拍 拍了一个多小时 虽然最后还是挂了 也不知道为什么没拍出来
然后去把 T3 五十分的 dp 写了 然后看 T1
半个小时把读入写了 然后才开始写题 毒瘤
先写了五十分的暴跳 然后想正解 搞了一下奇怪的东西 最后都弃了 直接每个点开了个 vector 暴跳找环 判循环节 再判个重 好 不管了
然后两个暴跳挂上开始拍...
然后把 T3 的转移写成了最短路 希望能减点状态 多过几分 虽然并没有什么软用
考试结束评测的时候
Ariel(看着 BS 写的 T1 的暴跳): 这你要是能过我直接把评测姬吃下去
发出了壮烈的言论.jpg
得分情况
100 + 70 + 50 = 220
T3 并没有多过几分
T2 挂掉了三十分...
T1 Ariel 欠 BS 一个评测姬
题解
显然没有题解 所以 T3 到现在没写出来 再次模一下陈绿大佬
T1 环
看一眼题大概就知道重点是环
显然可以直接暴跳五十分
每个点一定在一个环里 所以在从一个点往后跳的时候 跳的次数对环长取模不影响答案
这里 BS 的实现是 预处理每个点一下信息 维护每个点所在的环 维护每个点所在的环的长度 在每个环中找一个点作为环的起点对环上的点进行编号 维护每个点在环上的编号 维护从一个环的起点跳小于环长的任意步所能到达的点
然后就可以直接做了
代码
/*
Source: 环
*/
#include<cstdio>
#include<vector>
#include<cstring>
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
freopen("kengdie.in", "r", stdin);
freopen("kengdie.out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, d[B << 2], dcnt = 1, id[B], len[B], rt[B];
struct node {int x, y;} q[B];
bool vis[B];
std::vector <int> a[B];
/*----------------------------------------------------------*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
bool check(int x) {
for(int i = 1; i ^ dcnt + 1; ++i) vis[i] = 0;
for(int i = 1; i ^ x + 1; ++i) if(!vis[d[i]]) vis[d[i]] = 1; else return 0;
return 1;
}
void pre() {
int l = 0, r = dcnt, tmp = 0;
while(l <= r)
{
int mid = l + r >> 1;
if(check(mid)) tmp = mid, l = mid + 1;
else r = mid - 1;
}
n = tmp; m = dcnt - n >> 1;
for(int i = n + 1; i ^ dcnt + 1; ++i)
if(i - n & 1) q[i - n + 1 >> 1].x = d[i];
else q[i - n + 1 >> 1].y = d[i];
}
void work1() {
for(int i = 1; i ^ m + 1; ++i)
{
int pos = q[i].x;
for(int j = 1; j ^ q[i].y + 1; ++j) pos = d[pos];
Print(pos); pn;
}
}
void work2() {
for(int i = 1; i ^ n + 1; ++i) vis[i] = 0;
for(int i = 1, j, k; i ^ n + 1; ++i) if(!vis[i])
{
if(d[i] == i) {a[i].push_back(i); vis[i] = 1; rt[i] = i; id[i] = len[i] = 1; continue;}
a[i].push_back(i); id[i] = 1; rt[i] = i; vis[i] = 1;
for(j = 2, k = d[i]; k ^ i; ++j, k = d[k]) rt[k] = i, id[k] = j, a[i].push_back(k);
--j; len[i] = j;
for(k = d[i]; k ^ i; k = d[k]) len[k] = j, vis[k] = 1;
}
for(int i = 1; i ^ m + 1; ++i)
{
int pos = q[i].x, cnt = (id[pos] + q[i].y - 1) % len[pos];
Print(a[rt[pos]][cnt]); pn;
}
}
void Main() {
File();
while(~scanf("%d", &d[dcnt])) ++dcnt; --dcnt; pre();
// if(m <= 1010) work1();
// else work2();
work2();
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}
T2 礼
看着数据范围挺折半的 直接折半搜索 然后二分合并即可
直接找加和小于 \(M\) 的最大值即可 不需要考虑其他的情况 取最大值即可通过
要加和的两个数都是取过模的 加和之后不会超过 \(2M\) 而当两个数加和超过 \(M\) 的时候 取模之后反而不如一个数优 而一个数的情况会再其他状态里面被考虑到
代码
/*
Source: 礼
50 的爆搜
折半 前一半搜完排序 搜后一半 前面二分匹配
二分最大相加小于 M 的位置
考虑剩下的
两个数都小于 M 相加不可能大于 2M
二分最大相加小于 2M 的位置
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
freopen("gift.in", "r", stdin);
freopen("gift.out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, mod, a[40], S[(1 << 18) + 3], ans;
/*----------------------------------------------------------*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void dfs1(int t, int sum) {
if(t == n + 1) {ans = Max(ans, sum); return ;}
dfs1(t + 1, (sum + a[t]) % mod); dfs1(t + 1, sum);
}
void work1() {dfs1(1, 0); Print(ans);}
void work2() {
int x = n >> 1, y = n - x;
for(int i = 0; i < 1 << x; ++i) for(int j = 1; j ^ x + 1; ++j)
if(1 << j - 1 & i) S[i] = (S[i] + a[j]) % mod;
std::sort(S, S + (1 << x));
for(int i = 0; i < 1 << y; ++i)
{
int sum = 0, l, r, tmp;
for(int j = x + 1; j ^ n + 1; ++j) if(1 << j - x - 1 & i) sum = (sum + a[j]) % mod;
l = 0; r = (1 << x) - 1; tmp = l;
while(l <= r)
{
int mid = l + r >> 1;
if(sum + S[mid] < mod) tmp = mid, l = mid + 1;
else r = mid - 1;
}
sum += S[tmp]; ans = Max(ans, sum);
// l = 0; r = (1 << x) - 1; tmp = l;
// while(l <= r)
// {
// int mid = l + r >> 1;
// if(sum + S[mid] < 2 * mod) tmp = mid, l = mid + 1;
// else r = mid - 1;
// }
// sum = (sum + S[tmp]) % mod; ans = Max(ans, sum);
}
Print(ans);
}
void Main() {
// File();
n = read(); mod = read();
for(int i = 1; i ^ n + 1; ++i) a[i] = read();
// if(n <= 16) work1();
// else work2();
work2();
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}
T3 变
以下来自陈绿:
分两种情况:
\(a_i>A\) 这种直接完全用都没有, \(A-A=0\),扔掉。
\(a_i\leq A\) ,继续分成两种情况:
- \(A-A\%a_i\geq B\) 合法,留下
- \(A-A\%a_i<B\) 完全不合法,证明如下:
我们考虑:\(A-A\%a_i = A-(A- \left \lfloor\frac{A}{a_i} \right \rfloor\times a_i)\),也就是 \(\left \lfloor\frac{A}{a_i} \right \rfloor\times a_i<B\)。
那么可能有疑惑的是,这个会不会在 \(A\) 变小的时候会 \(>B\),显然不会,\(\left \lfloor\frac{A}{a_i} \right \rfloor\times a_i\) 当 \(A\) 变小时,这个值只会更小,因此更不会有贡献。
代码
/*
Source: T200528 change
*/
#include<set>
#include<cstdio>
#define IT std::set <int> :: iterator
#define Min(x, y) ((x) < (y) ? (x) : (y))
/*----------------------------------------------------------*/
const int C = 1e5 + 7;
/*----------------------------------------------------------*/
int n, A, B, ans, st[C], top;
std::set <int> t;
/*----------------------------------------------------------*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void Main() {
n = read();
for(int i = 1; i ^ n + 1; ++i) t.insert(read());
A = read(); B = read();
while(A > B)
{
int tmp = A - 1; top = 0;
for(IT i = t.begin(); i != t.end(); ++i)
{
int x = *i, y = A - A % x;
if(y < B) st[++top] = x; else tmp = Min(tmp, y);
}
for(int i = 1; i ^ top + 1; ++i) t.erase(st[i]);
A = tmp; ++ans;
}
Print(ans);
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}