冲刺国赛模拟 19
感觉越来越菜了。实在是懒得思考。不知道为啥。
现在有动力学数学了,但是是纯数。还是没动力学 oi。
矩阵
签到题。正解二维分块,\(O(\sqrt{nm})\) 修改 \(O(1)\) 查询。事实上二维树状数组可以艹过去。我是对每一行开树状数组的写法。这题有不少奇异写法来着。
见证了本机 2.1s tlecoders 0.45s 的奇异现象。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <cstring>
#include <ctime>
using namespace std;
int n,m,q;
struct BIT{
int c[1010];
#define lowbit(x) (x&-x)
void update(int x,int val){
while(x<=m)c[x]+=val,x+=lowbit(x);
}
int query(int x){
int sum=0;
while(x)sum+=c[x],x-=lowbit(x);
return sum;
}
}c[1010];
int query(int x,int l,int r){
return c[x].query(r)-c[x].query(l-1);
}
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
return x*f;
}
signed main(){
n=read();m=read();q=read();
for(int i=1;i<=q;i++){
int od=read(),x=read(),y=read();
if(od==1){
int z=read();
for(int j=y;j<=n;j++)c[j].update(x,z);
}
else{
int a=read(),b=read();
if(x>a)swap(x,a);
if(y>b)swap(y,b);
int ans=query(b,x,a)-query(y-1,x,a);
printf("%lld\n",ans);
}
}
return 0;
}
种草
为啥没想到费用流。
首先对于 \(a\) 全都相等有个套路的费用流模型:顺序往下一个点连 \(a\) 容量边,然后每个方案从 \(l\to r+1\) 连容量 \(1\) 代价 \(w\) 的边,跑最大费用最大流。
现在 \(a\) 没限制了,于是我们就是有了若干 \(S\) 往后连的必选的方案。实际上直接连就行了,因为最大费用一定会选上。
这是 \(10^5\) 个点边的流,但是边权只有 \(20\),因此最多跑 \(20\) 次最短路。
出题人不做人,卡 spfa,要写原始对偶。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <cstring>
#define int long long
using namespace std;
int n,m,S,T,ans,cost,a[100010],h[100010];
struct node{
int u,v,w,val,next;
int getval(){
return h[u]-h[v]+val;
}
}edge[1000010];
int t=1,head[100010];
void Add(int u,int v,int w,int val){
edge[++t].v=v;edge[t].u=u;edge[t].w=w;edge[t].val=val;edge[t].next=head[u];head[u]=t;
}
void add(int u,int v,int w,int val){
Add(u,v,w,val);Add(v,u,0,-val);
}
int dis[100010],pre[100010],flow[100010];
bool vis[100010];
struct stu{
int x,w;
bool operator<(const stu&s)const{
return w>s.w;
}
};
priority_queue<stu>q;
bool dijkstra(int s,int t){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(flow,0,sizeof(flow));
q.push({s,0});
dis[s]=0;flow[s]=0x3f3f3f3f;
while(!q.empty()){
int x=q.top().x;q.pop();
if(!vis[x]){
vis[x]=true;
for(int i=head[x];i;i=edge[i].next){
if(edge[i].w&&dis[edge[i].v]>dis[x]+edge[i].getval()){
dis[edge[i].v]=dis[x]+edge[i].getval();
flow[edge[i].v]=min(flow[x],edge[i].w);
pre[edge[i].v]=i;
q.push({edge[i].v,dis[edge[i].v]});
}
}
}
}
if(flow[t]==0)return false;
for(int x=t;x!=s;x=edge[pre[x]].u)edge[pre[x]].w-=flow[t],edge[pre[x]^1].w+=flow[t];
return true;
}
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);T=n+1;S=n+2;
for(int i=1;i<=n;i++)add(i,i+1,a[i],0);
for(int i=1;i<=n;i++)if(a[i]!=a[i-1])add(S,i,a[i]-a[i-1],0);
for(int i=1;i<=m;i++){
int l,r,w;scanf("%lld%lld%lld",&l,&r,&w);
add(l,r+1,1,-w);
}
memset(h,0x3f,sizeof(h));
h[S]=0;
for(int i=head[S];i;i=edge[i].next){
if(edge[i].w)h[edge[i].v]=min(h[edge[i].v],0ll);
}
for(int x=1;x<=n;x++){
for(int i=head[x];i;i=edge[i].next){
if(edge[i].w)h[edge[i].v]=min(h[edge[i].v],h[x]+edge[i].val);
}
}
if(h[0]==h[T]){
puts("0");return 0;
}
while(dijkstra(S,T)){
for(int i=1;i<=n+2;i++)h[i]+=dis[i];
ans+=flow[T];cost+=flow[T]*h[T];
}
printf("%lld\n",-cost);
return 0;
}
基环树
绪山真寻。现在内心真的是个爷们吗。不好说。
树的情况可以设 \(dp_{0,1,2}\) 为剩下几条边的一节的价值和,合并的转移是个背包。然而直接做容量是 \(O(n)\) 的过不了,我们按照 https://www.luogu.com.cn/blog/EntropyIncreaser/sui-ji-bian-liang-qian-zhui-hu-di-kong-zhi 的结论,随机化之后只有 \(O(\sqrt n)\) 的容量,就能直接做了。
基环树上就对环边大力讨论组成路径上哪一条边或者不用就好了。(dfs 四遍)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <cstring>
#include <ctime>
#include <random>
#define int long long
using namespace std;
mt19937 rnd(time(0)^(unsigned long long)(new char));
int n,ans,dp[100010][3],f[100010],h[100010];
vector<pair<int,int> >g[100010];
bool vis[100010];
int S,T,val;
void dfs1(int x,int f){
vis[x]=true;
for(pair<int,int> p:g[x]){
int v=p.first,w=p.second;
if(v==f)continue;
if(vis[v]){
S=x,T=v,val=w;
}
else dfs1(v,x);
}
vis[x]=false;
}
void dfs(int x,int fa,int tp){
for(pair<int,int> p:g[x]){
int v=p.first,w=p.second;
if(v==fa)continue;
if(v==S&&x==T)continue;
if(v==T&&x==S)continue;
dfs(v,x,tp);
}
int size=(int)sqrt(g[x].size())+1<<2;
int len=size>>1;
for(int i=0;i<=size;i++)f[i]=h[i]=-0x3f3f3f3f;
f[len]=0;
for(pair<int,int> p:g[x]){
int v=p.first,w=p.second;
if(v==fa)continue;
if(v==S&&x==T)continue;
if(v==T&&x==S)continue;
for(int i=0;i<=size;i++)h[i]=f[i]+max(dp[v][0],dp[v][2]+w);
for(int i=0;i<size;i++)h[i+1]=max(h[i+1],f[i]+dp[v][0]+w);
for(int i=1;i<=size;i++)h[i-1]=max(h[i-1],f[i]+dp[v][1]+w);
for(int i=0;i<=size;i++)f[i]=h[i];
}
if(x==T){
if(tp==0)for(int i=0;i<=size;i++)h[i+1]=f[i]+val;
if(tp==1)for(int i=1;i<=size;i++)h[i-1]=f[i]+val;
if(tp==2)for(int i=0;i<=size;i++)h[i]=f[i]+val;
for(int i=0;i<=size;i++)f[i]=h[i];
}
dp[x][0]=f[len];
dp[x][1]=f[len+1];
dp[x][2]=f[len-1];
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
int u,v,w;scanf("%lld%lld%lld",&u,&v,&w);
g[u].push_back(make_pair(v,w));g[v].push_back(make_pair(u,w));
}
for(int i=1;i<=n;i++)shuffle(g[i].begin(),g[i].end(),rnd);
dfs1(1,0);
dfs(S,0,0);
ans=max(ans,dp[S][0]);
dfs(S,0,1);
ans=max(ans,dp[S][1]);
dfs(S,0,2);
ans=max(ans,dp[S][2]);
dfs(S,0,3);
ans=max(ans,dp[S][0]);
printf("%lld\n",ans);
return 0;
}
快踩