A层省选2
A. 货币
线段树维护表示下一个同色的位置,表示以为右端点的最大左端点
有
使用启发式合并进行维护
由于单调,我们线段树二分出以为的这个区间,进行分裂
具体实现的一些细节:
-
不用真的维护出来,利用定义式可以快速求出
-
先更新nxt再查找0 - i最大的nxt,这样略去了不由新的nxt贡献的区间
-
在设置一个哨兵,记录最后一个出现的颜色的最左端,每次时保证一定包含所有颜色
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<random>
#include<vector>
#include<set>
using namespace std;
const int maxn = 3e5 + 10;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
int n, q, T, las, mi;
set<int> s[maxn];
struct SET{
int f[maxn];
void init(){for(int i = 1; i <= n; ++i)f[i] = i;}
int fa(int x){return f[x] = f[x] == x ? x : fa(f[x]);}
}S;
int nxt[maxn];
struct tree{
int mx[maxn << 2 | 1];
void modify(int x, int l, int r,int pos,int val){
if(l == r){nxt[l] = mx[x] = val; return;}
int mid =(l + r) >> 1;
if(pos <= mid)modify(x << 1, l, mid, pos, val);
else modify(x << 1 | 1, mid + 1, r, pos, val);
mx[x] = max(mx[x << 1], mx[x << 1 | 1]);
}
int query(int x, int l, int r, int L, int R){
if(L > R)return 0;
if(L <= l && r <= R)return mx[x];
int mid = (l + r) >> 1, ans = 0;
if(L <= mid) ans = max(ans, query(x << 1, l, mid, L, R));
if(R > mid) ans = max(ans, query(x << 1 | 1, mid + 1, r, L, R));
return ans;
}
int find(int x, int l, int r, int val){
if(l == r)return l;
int mid = (l + r) >> 1;
if(mx[x << 1] > val)return find(x << 1, l, mid, val);
else return find(x << 1 | 1, mid + 1, r, val);
}
}t;
void solve(int pos, int val){
int r = nxt[pos], now = 0;
t.modify(1, 0, n + 1, pos, val);
int pmi = t.query(1, 0, n + 1, 0, pos);
while(now <= n && nxt[now] <= r){
now = t.find(1, 0, n + 1, pmi);
mi = min(mi, pmi - now + 1);
pmi = nxt[now];
}
}
void work(int u, int v){
if(u > v)swap(u ,v);
if(s[u].size() < s[v].size())swap(s[u], s[v]);
for(int i : s[v])s[u].insert(i);
for(int i : s[v]){
auto it = s[u].find(i);
if(it != s[u].begin()){
--it;
solve(*it, i);
++it;
}
if(it != prev(s[u].end())){
++it;
solve(i, *it);
}
}
s[v].clear();
S.f[v] = u;
int pos = nxt[0];
while(s[pos].size() == 0)--pos;
solve(0, pos);
}
int main(){
freopen("currency.in", "r",stdin);
freopen("currency.out", "w",stdout);
n = read(), q = read(), T = read();
mi = n;
for(int i = 1; i <= n; ++i)t.modify(1, 0, n + 1, i, maxn);
for(int i = 1; i <= n; ++i)s[i].insert(i);
t.modify(1, 0, n + 1, 0, n);
S.init();
for(int i = 1; i <= q; ++i){
int u = (read() + T * las - 1) % n + 1;
int v = (read() + T * las - 1) % n + 1;
u = S.fa(u); v = S.fa(v);
if(u != v)work(u, v);
printf("%d\n",las = mi);
}
return 0;
}
B. 比赛
发现当并且改完一定赢才会改
那么发现如果没有人能把他改完的值(一定等于他自己)改掉,那么他才会改
如果一个人没有被限制,可以通过找出他限制谁改,最后找出最前面的答案即可
但是还需要用优化啥的,我还不会,先跑了
暴力
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<random>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 300005;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
int n, q, a[maxn], cnt[maxn];
set<int>s;
void match(){
int nans = 0;
for(int i = n - 1; i >= 1; --i)
if(cnt[i] == 0){
if(a[i] % n == i)nans = i, s.insert(i);
int to = (i - a[i] + n) % n;
if(to < i)++cnt[to];
}
printf("%d\n",nans);
}
void del(int x);
void add(int x);
void add(int x){
if(x == 0)return;
++cnt[x];
if(cnt[x] == 1){
if(a[x] == x)s.erase(x);
else{
int to = (x - a[x] + n) % n;
if(to < x)del(to);
}
}
}
void del(int x){
if(x == 0)return;
--cnt[x];
if(cnt[x] == 0){
if(a[x] == x)s.insert(x);
else{
int to = (x - a[x] + n) % n;
if(to < x)add(to);
}
}
}
void upd(int x, int y){
if(cnt[x] == 0){
if(a[x] == x)s.erase(x);
else {
int lasto = (x - a[x] + n) % n;
if(lasto < x) del(lasto);
}
if(y == x && x)s.insert(x);
else{
int to = (x - y + n) % n;
if(to < x) add(to);
}
}
a[x] = y;
if(s.empty())printf("0\n");else printf("%d\n",*s.begin());
}
int main(){
freopen("match.in", "r", stdin);
freopen("match.out", "w", stdout);
n = read(), q = read();
for(int i = 0; i < n; ++i)a[i] = read();
match();
for(int i = 1; i <= q; ++i){
int x = read(), y = read();
upd(x, y);
}
return 0;
}
C. 字符串
如何找根?shuffle
一定存在既在树又在树上的边,这些边都是从根节点出发的,而且一条链上的字符完全相同
那么,如果存在一个点度数大于2,那么他一定是根
如果不存在,那么就会有一条链,他是这样的串,考虑他在树上不在链上的邻居,一定是这样的串,这个邻居的要么还是类似的邻居(这样我们不管),要么在链上,代表或者空串,那么他要么是根,要么与根是邻居,只有三个点,可以暴力一下
具体check,应该先处理边上的信息,再建自动机,但是为了省事,可以根据树上的父亲在
树上深度小于它这个必要条件判一下
如果你这样写没过,可以试试倒叙扫shuffle等乱搞做法,
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<random>
#include<vector>
#include<set>
#include<iostream>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 300005;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
int n, ans[maxn];
struct tree{
int head[maxn], tot;
struct edge{int to, net;}e[maxn << 1 | 1];
void add(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
}
int dep[maxn], fa[maxn];
void dfs(int x){
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa[x])continue;
dep[v] = dep[x] + 1;
fa[v] = x;
dfs(v);
}
}
}trie, fail;
queue<int>q;
int mx, root;
void getans(){
q.push(root);
while(!q.empty()){
int x = q.front(); q.pop();
for(int i = trie.head[x]; i; i = trie.e[i].net){
int v = trie.e[i].to;
if(v == trie.fa[x])continue;
if(fail.fa[v] != root){
ans[v] = ans[fail.fa[v]];
}else ans[v] = ++mx;
q.push(v);
}
}
printf("%d\n",root);
for(int i = 1; i <= trie.tot; i += 2){
int u = trie.e[i].to, v = trie.e[i + 1].to;
if(trie.dep[u] < trie.dep[v])swap(u, v);
printf("%d ",ans[u]);
}
printf("\n");
}
int p[maxn];
bool check(int nrt){
trie.dep[nrt] = 1; trie.fa[nrt] = 0; trie.dfs(nrt);
fail.dep[nrt] = 1; fail.fa[nrt] = 0; fail.dfs(nrt);
trie.dep[0] = 0;
for(int i = 1; i <= n; ++i)if(trie.dep[fail.fa[i]] >= trie.dep[i] || trie.dep[fail.fa[trie.fa[i]]] + 1 < trie.dep[fail.fa[i]])return false;
return true;
}
int cnt[maxn];
bool vis[maxn];
int work1(){
for(int x = 1; x <= n; ++x){
for(int i = trie.head[x]; i; i = trie.e[i].net) vis[trie.e[i].to] = 1;
for(int i = fail.head[x]; i; i = fail.e[i].net) { int v = fail.e[i].to; if(vis[v]) ++cnt[v];}
for(int i = trie.head[x]; i; i = trie.e[i].net) vis[trie.e[i].to] = 0;
}
for(int i = 1; i <= n; ++i) if(cnt[i] > 2)return i;
int rt = 0;
for(int x = 1; x <= n; ++x){
if(cnt[x]){
for(int i = trie.head[x]; i; i = trie.e[i].net){
int y = trie.e[i].to;
if(!cnt[y]){
for(int j = fail.head[y]; j; j = fail.e[j].net){
int z = fail.e[j].to;
if(cnt[z]){rt = z;break;}
}
}
if(rt)break;
}
}
if(rt)break;
}
if(check(rt))return rt;
for(int i = trie.head[rt]; i; i = trie.e[i].net){
int y = trie.e[i].to;
if(cnt[y] && check(y))return y;
}
return rt;
}
int main(){
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
n = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
trie.add(u, v); trie.add(v, u);
}
for(int i = 1; i < n; ++i){
int u = read(), v = read();
fail.add(u, v); fail.add(v, u);
}
root = work1();
trie.dep[root] = 1; trie.fa[root] = 0; trie.dfs(root);
fail.dep[root] = 1; fail.fa[root] = 0; fail.dfs(root);
getans();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】