NOIP2023模拟7,8联测28,29
NOIP2023模拟7联测28
T1
看到了有向无环图,很容易让人想到拓扑。
设
如果有一个点满足
转移就是从所有能到达
复杂度为
Code
#include <iostream>
#include <cstdio>
#include <queue>
const int N=1e6+10;
using namespace std;
inline int read() {
int f=1, x=0;
char ch=getchar();
while(ch>'9' || ch<'0') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f*x;
}
inline void write(bool x) {
if(x) cout<<"Yes";
else cout<<"No";
putchar('\n');
}
int T;
int n, m, k, cnt_edge;
struct edge {
int next, to;
}e[N<<1];
int head[N], in[N];
int vis[N], val[N];
void add_edge(int u,int v) {
e[++cnt_edge].to=v;
e[cnt_edge].next=head[u];
head[u]=cnt_edge;
in[v]++;
}
inline void init() {
for(int i=1;i<=n;i++) {
head[i]=in[i]=0;
vis[i]=val[i]=0;
}
for(int i=1;i<=cnt_edge;i++) {
e[i].to=e[i].next=0;
}
n=cnt_edge=0;
}
queue <int> q;
void topu() {
for(int i=1;i<=n;i++) {
if(!in[i]) q.push(i);
}
while(!q.empty()) {
int now=q.front(); q.pop();
vis[now]+=val[now];
for(int i=head[now];i;i=e[i].next) {
int v=e[i].to;
vis[v]=max(vis[v], vis[now]);
in[v]--;
if(!in[v]) q.push(v);
}
}
bool flag=0;
for(int i=1;i<=n;i++) {
if(vis[i]==k) {
flag=1;
break;
}
}
write(flag);
}
int main() {
T=read();
while(T--) {
init();
n=read(), m=read();
for(int i=1;i<=m;i++) {
int u=read(), v=read();
add_edge(u, v);
}
k=read();
for(int i=1;i<=k;i++) {
int x=read();
val[x]=1;
}
topu();
}
return 0;
}
T2
Part1
区间修改不考虑,考虑差分,原序列为
Part2
我们把
我们选
- 当且仅当连通块内的点一开始的异或和为
时,才可以达到下界。
必要性:因为进行了
次操作,那么每次操作必然是修改两个,总的异或和不变,要想通过修改使其都变成 , 那么这几个数一开始的异或和必然为 。 充分性:如果异或和为
,那么可以把这几个点穿成一条链,每次把链头通过异或自己的方式把自己变为 ,这样最后一个数就变成了这几个数的异或和,为 。
那么我们就是想让这样的连通块最多,设其为
Part3
现在问题变成了:给定一个序列,把其划分成若干个子序列,最大化异或和为
复杂度证明
考虑每个状态被枚举了几次。
设当前状态集合为
由此可得总的次数为:
二项式反演即为:
复杂度为
Code
#include <iostream>
#include <cstdio>
#define int long long
const int N=18;
const int M=(1<<N)+10;
using namespace std;
inline int read() {
int f=1, x=0;
char ch=getchar();
while(ch>'9' || ch<'0') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f*x;
}
inline void write(int x) {
cout<<x; putchar('\n');
}
int n, ans;
int a[N], b[N], dp[M];
bool f[M];
signed main() {
n=read();
for(int i=1;i<=n;i++) {
a[i]=read(); b[i]=a[i]^a[i-1];
}
for(int s=0;s<(1<<n);s++) {
int sum=0;
for(int i=1;i<=n;i++) {
if(s & (1<<(i-1))) sum^=b[i];
}
if(!sum) f[s]=1;
}
for(int s=1;s<(1<<n);s++) {
for(int t=s;t;t=(t-1)&s) {
if(f[t]) {
dp[s]=max(dp[s], dp[s^t]+1);
}
}
}
write(n-dp[(1<<n)-1]);
return 0;
}
T3
Part1
先考虑特殊性质
Part2
原题又多了一部分贡献,我们可以考虑固定一部分贡献,另一部分取
设当前点分治的分治重心为
对于修改操作
对于查询操作
因为是取
复杂度为
Code
#include <iostream>
#include <cstdio>
#include <vector>
#define int long long
const int N=2e5+10;
const int inf=1e18;
using namespace std;
inline int read() {
int f=1, x=0;
char ch=getchar();
while(ch>'9' || ch<'0') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f*x;
}
inline void write(int x) {
cout<<x; putchar('\n');
}
int n, m, cnt_edge;
struct edge {
int next, to, val;
}e[N<<1];
int head[N];
struct node {
int opt, x, y;
}a[N];
vector <int> q[N];
int ans[N];
inline void add_edge(int u,int v,int w) {
e[++cnt_edge].to=v;
e[cnt_edge].val=w;
e[cnt_edge].next=head[u];
head[u]=cnt_edge;
}
int dep[N];
struct Heavy {
int size[N], son[N], fa[N], top[N];
void dfs1(int now,int father) {
fa[now]=father, size[now]=1;
int maxson=-1;
for(int i=head[now];i;i=e[i].next) {
int v=e[i].to;
if(v==father) continue;
dep[v]=dep[now]+e[i].val;
dfs1(v, now);
size[now]+=size[v];
if(maxson < size[v]) {
maxson=size[v], son[now]=v;
}
}
}
void dfs2(int now,int topf) {
top[now]=topf;
if(son[now]) dfs2(son[now], topf);
for(int i=head[now];i;i=e[i].next) {
int v=e[i].to;
if(v==fa[now] || v==son[now]) continue;
dfs2(v, v);
}
}
void prework() {
dfs1(1, 0);
dfs2(1, 1);
}
inline int LCA(int x,int y) {
while(top[x]!=top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x, y);
return x;
}
inline int Dis(int x,int y) {
int lca=LCA(x, y);
int res=dep[x]+dep[y]-2*dep[lca];
return res;
}
}H;
struct Part {
int root, sum_root;
int maxp[N], size[N], fa[N], mi[N], bin[N];
bool vis[N];
void get_root(int now,int fa) {
maxp[now]=0, size[now]=1;
for(int i=head[now];i;i=e[i].next) {
int v=e[i].to;
if(v==fa || vis[v]) continue;
get_root(v, now);
size[now]+=size[v];
maxp[now]=max(maxp[now], size[v]);
}
maxp[now]=max(maxp[now], sum_root-maxp[now]);
if(maxp[now] < maxp[root]) root=now;
}
void build(int now) {
vis[now]=1;
for(int i=head[now];i;i=e[i].next) {
int v=e[i].to;
if(vis[v]) continue;
sum_root=size[v], root=0;
get_root(v, now);
fa[root]=now;
build(root);
}
}
void build() {
maxp[0]=n+1, sum_root=n;
root=0, get_root(1, 0);
build(root);
}
inline void up_date(int x,int y,int rt) {
int len=H.Dis(x, rt);
for(int i=y;i;i=fa[i]) {
int dis=H.Dis(i, y)+len;
mi[i]=min(mi[i], dis);
}
}
inline int query(int x,int y,int rt) {
int len=H.Dis(x, rt), res=inf;
for(int i=y;i;i=fa[i]) {
int val=mi[i]+H.Dis(i, y);
res=min(res, val+len);
}
return res;
}
void clear(int now) {
for(int i=now;i;i=fa[i]) mi[i]=inf;
}
void calc(int now) {
int tot=0;
for(auto &p : q[now]) {
if(a[p].opt==1) {
bin[++tot]=a[p].y;
up_date(a[p].x, a[p].y, now);
}
if(a[p].opt==2) {
int res=query(a[p].x, a[p].y, now);
ans[p]=min(ans[p], res);
}
}
for(int i=1;i<=tot;i++) clear(bin[i]);
}
void solve(int now) {
vis[now]=1;
calc(now);
for(int i=head[now];i;i=e[i].next) {
int v=e[i].to;
if(vis[v]) continue;
root=0, sum_root=size[v];
get_root(v, now);
solve(root);
}
}
void work() {
for(int i=1;i<=n;i++) vis[i]=0;
sum_root=n, maxp[0]=n+1;
root=0, get_root(1, 0);
solve(root);
}
}P;
void prework() {
H.prework();
P.build();
int lim=max(m, n);
for(int i=1;i<=lim;i++) {
ans[i]=P.mi[i]=inf;
}
}
void insert(int id) {
int tmp=a[id].x;
while(tmp) {
q[tmp].push_back(id);
tmp=P.fa[tmp];
}
}
signed main() {
n=read(), m=read();
for(int i=1;i<n;i++) {
int u=read(), v=read(), w=read();
add_edge(u, v, w);
add_edge(v, u, w);
}
prework();
for(int i=1;i<=m;i++) {
a[i]=(node){read(), read(), read()};
insert(i);
}
P.work();
for(int i=1;i<=m;i++) {
if(a[i].opt==1) continue;
if(ans[i]>=inf) write(-1);
else write(ans[i]);
}
return 0;
}
T4
不会,咕了。
NOIP2023模拟8联测29
T1
发现加入一个数一定不优,删去一个数一定不劣,那么考虑维护一个双指针,在移动左指针时移动右指针,发现右指针一定单调向右移。
维护一个线段树,维护值域上的最长连续段,指针边移边修改即可。
复杂度为
Code
#include <iostream>
#include <cstdio>
#define int long long
const int N=2e5+10;
using namespace std;
inline int read() {
int f=1, x=0;
char ch=getchar();
while(ch>'9' || ch<'0') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f*x;
}
inline void write(int x) {
cout<<x; putchar('\n');
}
int n, k;
int a[N];
struct Tree {
int sum, lc, rc, num;
}t[N<<2];
struct Seg_Tree {
inline void push_up(int k,int l,int r) {
int mid=(l+r)>>1;
int len=t[k<<1].rc+t[k<<1|1].lc;
t[k].sum=max(len, max(t[k<<1].sum, t[k<<1|1].sum));
if(t[k<<1].lc==(mid-l+1)) t[k].lc=t[k<<1].lc+t[k<<1|1].lc;
else t[k].lc=t[k<<1].lc;
if(t[k<<1|1].rc==(r-mid)) t[k].rc=t[k<<1|1].rc+t[k<<1].rc;
else t[k].rc=t[k<<1|1].rc;
}
void change(int k,int l,int r,int pos,int val) {
if(l==r) {
t[k].num+=val;
if(t[k].num) t[k].sum=t[k].lc=t[k].rc=1;
else t[k].sum=t[k].lc=t[k].rc=0;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) change(k<<1, l, mid, pos, val);
else change(k<<1|1, mid+1, r, pos, val);
push_up(k, l, r);
}
}T;
signed main() {
n=read(), k=read();
for(int i=1;i<=n;i++) a[i]=read();
if(k==0) {
write(0); return 0;
}
int r=0, ans=0;
for(int i=1;i<=n;i++) {
while(t[1].sum<=k && r<=n) {
r++;
if(r>n) break;
T.change(1, 1, n ,a[r], 1);
}
ans+=(r-i);
T.change(1, 1, n, a[i], -1);
}
write(ans);
return 0;
}
T2
没意思,咕了。
T3
不会,咕了。
T4
没看懂题,咕了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?