20261007做题记录
上午类模拟赛
T1:
简单模拟。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define intl long long
#define For(i,a,b) for(intl i=a;i<=b;i++)
#define deo(i,a,b) for(intl i=a;i>=b;i--)
intl read() {
intl x=0,k=1;char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*k;
}
intl n, h, l, r, ans;
int main() {
freopen("weight.in","r",stdin);
freopen("weight.out","w",stdout);
n = read(), h = read(), r = h;
For(i,1,n-1) {
intl b = read(), nl = max(0ll,b-r), nr = min(h,b-l);
if(nl > nr) ans++, l = 0, r = h;
else l = nl, r = nr;
}
printf("%lld\n",ans);
return 0;
}
T2:
我们发现可以分情况讨论。
- \(a_i > a_{i+1}\):
显然,答案不会变小,那么显然我们只需要预处理出 \(i+1\) 后面的第一个数比 \(a_i\) 大的最长上升子序列和 \(i\) 前面最后一个数比 \(a_{i+1}\) 小的最长上升子序列就可以了,答案为原来的和把找到的这两个子序列拼起来的长度+2最大值。 - \(a_i < a_{i+1}\)
显然,答案不会变大,我们发现答案什么时候变小,我们发现就是在原来长度的所有LIS都会经过 \(i\) 和 \(i+1\),这该怎么做,其实就是如果某一条 LIS 经过 \(i\) 那么有 \(f_i + g_i - 1 = len\) 其中,\(f_i\) 表示以 \(i\) 结尾的 LIS 长度,\(g_i\) 表示以 \(i\) 开头的 LIS 长度,\(len\) 为全局的 LIS 长度, 那么如果 \(cnt_{f_i} = 1\) 那么就代表所有 LIS 到会经过 \(i\),其中 \(cnt_{f_i}\) 表示所有 LIS 上的点以自己为结尾的LIS的长度为 \(f_i\) 数量,那么显然,如果数量为 \(1\) 则代表所有 LIS 都要经过自己,对 \(i\) 和 \(i+1\) 判断一下就可以了,答案为如果必经过就是原 \(LIS-1\) 否则就是 LIS。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define intl long long
#define For(i,a,b) for(int i=a;i<=b;i++)
#define deo(i,a,b) for(int i=a;i>=b;i--)
intl read() {
intl x=0,k=1;char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*k;
}
intl n, a[N], f[N], g[N], pre[N], suf[N], cnt[N], len;
struct BIT{
intl tr[N];
void add(intl x,intl v) {
for(;x<=n;x+=x&-x) tr[x] = max(tr[x],v);
}
intl ask(intl x) {
intl ans = 0;
for(;x;x-=x&-x) ans = max(ans,tr[x]);
return ans;
}
}bit;
bool check(intl x) {
return f[x]+g[x]-1 == len && cnt[f[x]] == 1;
}
int main() {
freopen("lis.in","r",stdin);
freopen("lis.out","w",stdout);
n = read();
For(i,1,n) a[i] = read();
For(i,1,n) {
if(i < n) pre[i] = bit.ask(a[i+1]-1);
f[i] = bit.ask(a[i]-1)+1;
bit.add(a[i],f[i]);
len = max(len,f[i]);
}
memset(bit.tr,0,sizeof bit.tr);
deo(i,n,1) {
if(i > 1) suf[i-1] = bit.ask(n-a[i-1]);
g[i] = bit.ask(n-a[i])+1;
bit.add(n-a[i]+1,g[i]);
}
For(i,1,n) if(f[i]+g[i]-1 == len) cnt[f[i]]++;
For(i,1,n-1) {
intl ans = len;
if(a[i] > a[i+1]) ans = max(ans,pre[i]+2ll+suf[i]);
else if(check(i) && check(i+1)) ans--;
printf("%lld ",ans);
}
return 0;
}
T3:
不会。
T4:
不会。
CF1385E
简单题,显然,我们可以先对有向边做拓扑排序,然后对于无向边我们将其定向为拓扑序标号小的指向大的就可以了,无解当且仅当原有向边构成环。
证明
显然,按照拓扑序之后,就可以发现我们可能存在拓扑序编号小的指向大的,但是一定不存在编号大的指向小的路径,那么如果我们像这样给无向边定向之后,就一定不存在环。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define intl long long
#define For(i,a,b) for(intl i=a;i<=b;i++)
#define deo(i,a,b) for(intl i=a;i>=b;i--)
intl read() {
intl x=0,k=1;char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*k;
}
intl n, m, id[N], deg[N];
vector<intl> g[N];
void solve() {
n = read(), m = read();
vector<pair<intl,intl>> vec, ans;
For(i,1,m) {
intl t = read(), u = read(), v = read();
if(t) ans.push_back({u, v}), g[u].push_back(v), deg[v] ++;
else vec.push_back({u, v});
}
intl idx = 0;
queue<intl> q;
For(i,1,n) if(!deg[i]) q.push(i);
while(q.size()) {
auto u = q.front();q.pop();
id[u] = ++idx;
for(auto v:g[u]) if(!--deg[v]) q.push(v);
}
if(idx != n) return puts("No"), void();
for(auto [u, v]:vec) {
if(id[u] < id[v]) ans.push_back({u, v});
else ans.push_back({v, u});
}
puts("Yes");
for(auto [u,v]:ans) printf("%lld %lld\n", u, v);
}
void clear() {
For(i,1,n) id[i] = deg[i] = 0, g[i].clear();
}
int main() {
intl T = read();
while(T --) solve(), clear();
return 0;
}
CF977E
简单题,问我们每个联通块是不是一个环,我们就只需要检查每个联通块里面是不是每个点的度数都等于2就可以了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define intl long long
#define For(i,a,b) for(int i=a;i<=b;i++)
#define deo(i,a,b) for(int i=a;i>=b;i--)
intl read() {
intl x=0,k=1;char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*k;
}
intl n, m, vis[N], que[N], ans;
vector<intl> g[N];
int main() {
n = read(), m = read();
For(i,1,m) {
intl u = read(), v = read();
g[u].push_back(v), g[v].push_back(u);
}
For(i,1,n) if(!vis[i]) {
intl l = 1, r = 1;
bool flag = true;
que[1] = i, vis[i] = 1;
while(l <= r) {
intl u = que[l++];
if(g[u].size() != 2) flag = false;
for(intl v:g[u]) if(!vis[v])
vis[v] = 1, que[++r] = v;
}
if(flag) ans++;
}
printf("%lld\n",ans);
return 0;
}
CF1473E
显然,式子为 \(\sum_{i=1}^k w_i - \max w_i + \min w_i\) 我们将 \(-w_i\) 和 \(+w_i\) 视为一种操作,因为我们求最短路,所以到最后一定是取的 \(-\max w_i\) 和 \(+\min w_i\) 因为这样才最小。我们定义 \(dis_{u,0/1/2/3}\) 其中 \(dis_{u,0}\) 表示一个操作都没有使用的最小值,\(dis_{u,1}\) 表示只使用减法的最小值,\(dis_{u,2}\) 表示只使用了加法的最小值,\(dis_{u,3}\) 表示都用了的最小值,那么答案就是 \(dis_{u,3}\)。转移就正常转移。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define intl long long
#define For(i,a,b) for(intl i=a;i<=b;i++)
#define deo(i,a,b) for(intl i=a;i>=b;i--)
intl read() {
intl x=0,k=1;char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*k;
}
struct node{
intl u, d, s;
bool operator < (const node&a) const {
return d > a.d;
}
};
intl dis[N][4], n, m;
vector<pair<intl,intl>> g[N];
priority_queue<node> q;
void upd(intl u,intl d,intl s) {
if(d < dis[u][s]) dis[u][s] = d, q.push({u, dis[u][s], s});
}
void dij() {
while(q.size()) {
auto [u,d,s] = q.top();q.pop();
if(dis[u][s] < d) continue;
for(auto [v,w]:g[u]) {
upd(v, w+d, s);
if(!(s&1)) upd(v, d, s|1);
if(!(s&2)) upd(v, d+2*w, s|2);
if(!s) upd(v, d+w, 3);
}
}
}
int main() {
n = read(), m = read();
For(i,1,m) {
intl u = read(), v = read(), w = read();
g[u].push_back({v, w}), g[v].push_back({u, w});
}
memset(dis, 127, sizeof dis), upd(1, 0, 0);
dij();
For(i,2,n) printf("%lld ", dis[i][3]);
return 0;
}
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
#define intl long long
#define For(i,a,b) for(intl i=a;i<=b;i++)
#define deo(i,a,b) for(intl i=a;i>=b;i--)
intl read() {
intl x=0,k=1;char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*k;
}
intl nxt[N], n, mx;
string s;
int main() {
cin >> s, n = s.size(), s = ' ' + s;
For(i,2,n) {
intl j = nxt[i-1];
while(j && s[i] != s[j+1]) j = nxt[j];
if(s[j+1] == s[i]) j ++;
nxt[i] = j;
if(i < n) mx = max(mx, nxt[i]);
}
intl ans = nxt[n];
while(ans > mx) ans = nxt[ans];
if(ans) For(i,1,ans) putchar(s[i]);
else puts("Just a legend");
return 0;
}
CF7D
简单题,显然前缀 \(i\) 的贡献等于 \(\frac{i}{2}\) 的贡献 \(+1\),且有贡献当且仅当这个前缀是回文串,然后就做完了,用哈希判断是不是回文串就可以了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 5000005
#define intl long long
#define For(i,a,b) for(intl i=a;i<=b;i++)
#define deo(i,a,b) for(intl i=a;i>=b;i--)
intl read() {
intl x=0,k=1;char ch=getchar();
while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*k;
}
const intl B1 = 233, mod1 = 19260817;
const intl B2 = 131, mod2 = 1000000007;
intl n, dp[N];
string s;
int main() {
cin >> s, n = s.size(), s = ' ' + s;
intl f1 = 0, f2 = 0, r1 = 0, r2 = 0, p1 = 1, p2 = 1, ans = 0;
For(i,1,n) {
(f1 += s[i]*p1%mod1) %= mod1, (f2 += s[i]*p2%mod2)%=mod2;
(r1 = r1*B1+s[i]) %= mod1, (r2 = r2*B2 + s[i]) %= mod2;
if(f1 == r1 && f2 == r2) dp[i] = dp[i/2] + 1;
ans += dp[i], (p1 *= B1) %= mod1, (p2 *= B2) %= mod2;
}
printf("%lld\n", ans);
return 0;
}

浙公网安备 33010602011771号