2024.10.04 刷题记录
2024.10.04 刷题记录
P6764 APIO2020 粉刷墙壁
不难发现题目大意是每次选一个点,向后覆盖一个长度为
那我们只关心从点
设
转移:
其中第
由于喜欢一个颜色的承包商小于
需要进行滚动数组优化空间。
算要求数,可以转化为拼接线段的模型,每次选出一个起点之后,前一个起点一定在
// #include"paint.h"
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+3;
int col[maxn],p[maxn],dp[2][maxn];
vector<int> s[maxn];
int minimumInstructions(int n,int m,int k,vector<int> c,vector<int> a,vector<vector<int>> b)
{
int ans=0;
for(int i=1;i<=n;++i) col[i]=c[i-1]+1;
for(int i=1;i<=m;++i) for(int j:b[i-1]) s[j+1].push_back(i);
for(int x,i=n;x=i&1,i;--i)
{
for(int j:s[col[i]]) p[i]|=(dp[x][j]=dp[!x][j%m+1]+1)>=m;
for(int j:s[col[i+1]]) dp[!x][j]=0;
}
for(int i=n-m+1;i;--i)
{
if(++ans,!p[i]) return -1;
for(int j=max(i-m,1);j<i;++j)
{
if(p[j])
{
i=j+1;
break;
}
}
}
return ans;
}
// int main()
// {
// int n,m,k;
// scanf("%d%d%d",&n,&m,&k);
// vector<int>c(n),a(m);
// vector<vector<int>>b(m);
// for(int i=0;i<n;++i) scanf("%d",&c[i]);
// for(int i=0;i<m;++i)
// {
// scanf("%d",&a[i]);b[i].resize(a[i]);
// for(int j=0;j<a[i];j++) scanf("%d",&b[i][j]);
// }
// printf("%d",minimumInstructions(n,m,k,c,a,b));
// return 0;
// }
P7929 COCI2021-2022#1 Logičari
基环树
考虑树证明处理,设
有转移:
对于叶子节点:
接着分类讨论一下通过多出来的边连接的两个点,其中其中一个为根(
下面的
-
: 。其中
表示 需要通过 的方程转移,下同。答案为
。 -
: 。答案为
。 -
: 。答案为
。 -
: 。答案为
。
若上述四种情况均为
否则取最小值即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1e9
const int maxn=1e5+5;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt;}edge[maxn*2];
inline void add(int x,int y)
{
tot++;
edge[tot].to=y;
edge[tot].nxt=head[x];
head[x]=tot;
}
}T;
int n,rt,rt_bro,flg;
int f[maxn];
ll ans=inf;
ll dp[maxn][2][2];
inline int fr(int u){return f[u]==u?u:f[u]=fr(f[u]);}
inline void dfs(int u,int f)
{
dp[u][0][1]=dp[u][1][1]=inf;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
dfs(v,u);
dp[u][0][0]+=dp[v][0][1];
if(dp[v][1][1]<inf) dp[u][0][1]=min(dp[u][0][1],dp[v][1][1]-dp[v][0][1]);
dp[u][1][0]+=dp[v][0][0];
if(dp[v][1][0]<inf) dp[u][1][1]=min(dp[u][1][1],dp[v][1][0]-dp[v][0][0]);
}
dp[u][0][1]=dp[u][0][0]+dp[u][0][1];
dp[u][1][0]++;
dp[u][1][1]=dp[u][1][0]+dp[u][1][1];
if(u==rt_bro)
{
if(flg==1)
{
dp[u][1][1]=inf,dp[u][1][0]=inf,dp[u][0][1]=dp[u][0][0],dp[u][0][0]=inf;
}
else if(flg==2)
{
dp[u][1][1]=dp[u][1][0],dp[u][1][0]=inf,dp[u][0][1]=inf,dp[u][0][0]=inf;
}
else if(flg==3)
{
dp[u][1][0]=inf,dp[u][1][1]=inf;
}
else
{
dp[u][0][0]=inf,dp[u][0][1]=inf;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
int fu=fr(u),fv=fr(v);
if(fu==fv) {rt=u,rt_bro=v;continue;}
T.add(u,v),T.add(v,u);
f[fu]=fv;
}
memset(dp,0,sizeof(dp));
flg=1;
dfs(rt,0);
ans=min(ans,dp[rt][1][1]);
memset(dp,0,sizeof(dp));
flg=2;
dfs(rt,0);
ans=min(ans,dp[rt][1][0]);
memset(dp,0,sizeof(dp));
flg=3;
dfs(rt,0);
ans=min(ans,dp[rt][0][1]);
memset(dp,0,sizeof(dp));
flg=4;
dfs(rt,0);
ans=min(ans,dp[rt][0][0]);
printf("%lld",ans<inf?ans:-1);
}
P10044 CCPC 2023 北京市赛 最小环
诈骗题。
先拓扑把出度或入度为
剩下的点至少是三度点,而一条边只能提供两度,满足
结合
这里的
需要特殊处理一下度为
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
#define pli pair<ll,int>
#define fi first
#define se second
const int maxn=3e5+5;
inline int read()
{
int x;
x=0;bool flag(0);char ch=getchar();
while(!isdigit(ch)) flag=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
flag?x=-x:0;
return x;
}
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt;ll w;}edge[maxn*2];
inline void add(int x,int y,int z)
{
tot++;
edge[tot].to=y;
edge[tot].w=z;
edge[tot].nxt=head[x];
head[x]=tot;
}
}G,fG;
int n,m;
int from[maxn],rd[maxn],cd[maxn];
bool vis[maxn],del[maxn];
ll ans=1e18;
ll dis[maxn];
inline void dij(int st)
{
for(int i=0;i<=n;i++) vis[i]=false,dis[i]=1e18;
dis[st]=0;
priority_queue<pli,vector<pli>,greater<pli>>que;
que.push({0,st});
while(!que.empty())
{
int u=que.top().se;que.pop();
if(vis[u]) continue;
vis[u]=true;
for(int i=G.head[u];i;i=G.edge[i].nxt)
{
int v=G.edge[i].to;
if(dis[v]>dis[u]+G.edge[i].w)
{
dis[v]=dis[u]+G.edge[i].w;
que.push({dis[v],v});
}
}
}
}
inline void bfs()
{
queue<int>que;
for(int i=1;i<=n;i++) if(!rd[i]||!cd[i]) que.push(i),vis[i]=true;
while(!que.empty())
{
int u=que.front();que.pop();
if(!rd[u])
for(int i=G.head[u];i;i=G.edge[i].nxt)
{
int v=G.edge[i].to;
rd[v]--;
if(!vis[v]&&!rd[v]) que.push(v),vis[v]=true;
}
else
for(int i=fG.head[u];i;i=fG.edge[i].nxt)
{
int v=fG.edge[i].to;
cd[v]--;
if(!vis[v]&&!cd[v]) que.push(v),vis[v]=true;
}
}
}
signed main()
{
// scanf("%d%d",&n,&m);
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int u,v,w;
// scanf("%d%d%d",&u,&v,&w);
u=read(),v=read(),w=read();
if(u==v) {ans=min(ans,(ll)w);continue;}
cd[u]++,rd[v]++;
G.add(u,v,w);
fG.add(v,u,G.tot);
}
bfs();
for(int i=1;i<=n;i++)
{
if(rd[i]==1&&cd[i]==1)
{
for(int j=fG.head[i];j;j=fG.edge[j].nxt)
{
int v=fG.edge[j].to;
if(!vis[v]){from[i]=fG.edge[j].w;break;}
}
for(int j=G.head[i];j;j=G.edge[j].nxt)
{
int v=G.edge[j].to;
if(!vis[v]){G.head[i]=j;break;}
}
}
}
for(int i=1;i<=n;i++)
{
if(cd[i]==1&&rd[i]==1&&G.edge[G.head[i]].to!=i)
{
G.edge[from[i]].to=G.edge[G.head[i]].to;
G.edge[from[i]].w+=G.edge[G.head[i]].w;
from[G.edge[G.head[i]].to]=from[i];
del[i]=true;
}
}
for(int u=1;u<=n;u++) for(int j=G.head[u];j;j=G.edge[j].nxt)
{
int v=G.edge[j].to;
if(v==u) ans=min(ans,G.edge[j].w);
}
int cnt=0;
for(int i=1;i<=n;i++)
{
if(rd[i]&&cd[i]&&!del[i])
{
cnt++;
assert(cnt<=4000);
dij(i);
for(int u=1;u<=n;u++) if(dis[u]^dis[0]) for(int j=G.head[u];j;j=G.edge[j].nxt)
{
int v=G.edge[j].to;
if(v==i) ans=min(ans,dis[u]+G.edge[j].w);
}
}
}
printf("%lld",ans==1e18?-1:ans);
}
P6604 HNOI2016 序列 加强版
P3246 HNOI2016 序列
转图论的笛卡尔树做法。
设
设
这里的
有
考虑一个查询,找到查询区间中最小的点
对于区间
对于
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+5;
namespace gen{
typedef unsigned long long ull;
ull s,a,b,c,lastans=0;
ull rand(){
return s^=(a+b*lastans)%c;
}
};
int n,m,tp;
int a[maxn],st[maxn][25],pre[maxn],nxt[maxn];
ll f[maxn],sf[maxn],g[maxn],sg[maxn];
inline int cmp(int x,int y){return a[x]<a[y]?x:y;}
inline int qry(int l,int r)
{
int k=__lg(r-l+1);
return cmp(st[l][k],st[r-(1<<k)+1][k]);
}
int main()
{
a[0]=2e9;
scanf("%d%d%d",&n,&m,&tp);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),st[i][0]=i;
for(int i=1;i<=20;i++) for(int j=1;j+(1<<i)-1<=n;j++)
st[j][i]=cmp(st[j][i-1],st[j+(1<<(i-1))][i-1]);
stack<int>stk;
for(int i=1;i<=n;i++)
{
while(!stk.empty())
{
if(a[stk.top()]>=a[i]) nxt[stk.top()]=i,stk.pop();
else break;
}
if(!stk.empty()) pre[i]=stk.top();
stk.push(i);
}
for(int i=1;i<=n;i++) f[i]=f[pre[i]]+1ll*a[i]*(i-pre[i]),sf[i]=sf[i-1]+f[i];
for(int i=n;i;i--) g[i]=g[nxt[i]]+1ll*a[i]*(nxt[i]-i),sg[i]=sg[i+1]+g[i];
if(tp) cin>>gen::s>>gen::a>>gen::b>>gen::c;
unsigned ll res=0;
for(int i=1,l,r;i<=m;i++)
{
if(tp==0) scanf("%d%d",&l,&r);
else {
l=gen::rand()%n+1;
r=gen::rand()%n+1;
if(l>r) swap(l,r);
}
ll p=qry(l,r),ans=0;
ans=a[p]*(r-p+1)*(p-l+1);
ans+=sf[r]-sf[p]-f[p]*(r-p);
ans+=sg[l]-sg[p]-g[p]*(p-l);
res^=ans;
gen::lastans=ans;
}
printf("%lld",res);
}
P6628 省选联考 2020 B 卷 丁香之路
必经
这些边和点放置于图中,此时若只有一个连通块,最优方案肯定希望
遂发现若
于是如下构造,先连一条权为
再考虑构造后分成了多个连通块,在连通块间求最小生成树,树边重复2次就是不同连通块的贡献(三角形的边长关系可以粗略证明)。
最后答案就是2倍树边加构造的边加最初
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pipii pair<int,pair<int,int>>
#define fi first
#define se second
const int maxn=2505;
int n,m,s;
int bel[maxn],f[maxn],deg[maxn];
ll sum;
inline int fr(int u){return f[u]==u?u:f[u]=fr(f[u]);}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
sum+=abs(u-v);deg[u]++,deg[v]++;
int fu=fr(u),fv=fr(v);
if(fu!=fv) f[fu]=fv;
}
for(int i=1;i<=n;i++) bel[i]=fr(i);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) f[j]=j;
deg[s]++,deg[i]++;
f[fr(bel[s])]=fr(bel[i]);
ll ans=sum;int pre=0;
for(int j=1;j<=n;j++)
{
if(deg[j]&1)
{
if(pre)
{
ans+=j-pre;
for(int k=pre;k<j;k++) f[fr(bel[k])]=fr(bel[j]);
pre=0;
}
else pre=j;
}
}
vector<pipii>vec;
for(int j=1;j<=n;j++)
{
if(deg[j])
{
if(pre&&fr(bel[j])!=fr(bel[pre])) vec.push_back({j-pre,{bel[j],bel[pre]}});
pre=j;
}
}
sort(vec.begin(),vec.end());
for(auto j:vec)
{
int u=j.se.fi,v=j.se.se;
int fu=fr(u),fv=fr(v);
if(fu!=fv) f[fu]=fv,ans+=j.fi*2;
}
deg[s]--,deg[i]--;
printf("%lld ",ans);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?