20260604紫题训练

P3212 HNOI2011 任务调度

题意

有 nnn 个任务,和两台机器 A,BA,BA,B,每个任务 iii 需要在 AAA 做 aia_iai 的时间,在 BBB 做 bib_ibi 的时间

任务有三种类型

1.\verb!1.!1. 类型 111

先在 AAA 做再去 BBB 做

2\verb!2!2 类型 222

先在 BBB 做再去 AAA 做

3.\verb!3.!3. 类型 333

可以任意归到类型 111 或类型 222

其中 n≤20n\leq 20n≤20,类型 333 的个数不超过 101010

赛时思路

考虑如果没有 333 怎么做

AAA 肯定先把所有 AAA 先的做了,BBB 同理

此时用时长的机器可以完美接上

而用时短的就需要具体考虑了

不妨令 AAA 机器是慢的,设 AAA 第一部分用了 ttt 单位时间

则对于 BBB 先的任务

若 bi≤ai,bi<=tb_i\leq a_i,b_i<=tbi≤ai,bi<=t

这种肯定放最前面做,做完后会产生新的时间优势 t′t't′,我们就可以用这个时间优势继续迭代

迭代完后所有任务要么 bi>tb_i>tbi>t 要么 ai>bia_i>b_iai>bi

于是我们将 >t>t>t 的放一组,≤t\leq t≤t 的放一组,


前面迭代的部分应该是没有问题的

剩下的考虑二分一个截至时间 TTT

算出 AAA 的起始时间 ststst

那么条件变为 ∀i,∑j=1ibj−aj<st\forall i,\sum_{j=1}^ib_j-a_j<st∀i,∑j=1ibj−aj<st

所以按 bj−ajb_j-a_jbj−aj 排序即可

发现前面的迭代是不需要的,放在后面一起做即可

而对于有 333 的情况,我们只需给每个 333 分配 111 或 222 即可

时间复杂度 O(2n×n)O(2^n\times n)O(2n×n)

题解

赛时思路已经基本没有问题了

只是最后式子列错了

应该是 ∑j=1ibj−aj+ai≤st\sum_{j=1}^ib_j-a_j+a_i\leq stj=1∑ibj−aj+ai≤st

二分 ststst ,然后贪心地往序列里塞能放的中 bj−ajb_j-a_jbj−aj 最小的

时间复杂度 O(2d×n2logn)O(2^d\times n^2logn)O(2d×n2logn),其中 ddd 为类型 333 的个数

代码

cpp 复制代码
#include<bits/stdc++.h>
# define Maxn 25
using namespace std;
int n;
struct Node{int t,x,y;}a[Maxn];
vector<int> vec[5];

bool vis[Maxn];
int p[Maxn];
int ans=2e9+1;
bool cmp(int x,int y) {return 2*a[vec[2][x]].y-a[vec[2][x]].x<2*a[vec[2][y]].y-a[vec[2][y]].x;}
struct Sol{
    void Solve() {
        bool IfSwap=0;
        int sum1=0,sum2=0;
        for(int i=0;i<vec[1].size();i++) sum1+=a[vec[1][i]].x;
        for(int i=0;i<vec[2].size();i++) sum2+=a[vec[2][i]].y;
        if(sum1>sum2) {
            IfSwap=1,swap(vec[1],vec[2]),swap(sum1,sum2);
            for(int i=0;i<vec[1].size();i++) swap(a[vec[1][i]].x,a[vec[1][i]].y);
            for(int i=0;i<vec[2].size();i++) swap(a[vec[2][i]].x,a[vec[2][i]].y);
        }

        // for(int i=0;i<vec[1].size();i++) printf("%d ",vec[1][i]);printf("\n");
        // for(int i=0;i<vec[2].size();i++) printf("%d ",vec[2][i]);printf("\n");
        // printf("%d %d\n",sum1,sum2);

        for(int i=0;i<vec[1].size();i++) sum2+=a[vec[1][i]].y;

        int l=0,r=2e9+1,mid,ST=0;
        while(l<=r) {
            mid=(l+r)>>1;
            
            int Cnt=0,Sum=0;
            for(int I=0;I<vec[2].size();I++) {
                int Mn=-1;
                for(int i=0;i<vec[2].size();i++) {
                    if((!vis[vec[2][i]])&&Sum+a[vec[2][i]].y<=mid) {
                        if(Mn==-1) Mn=vec[2][i];
                        else if(a[Mn].y-a[Mn].x>a[vec[2][i]].y-a[vec[2][i]].x) Mn=vec[2][i];
                    }
                }
                if(Mn==-1) break;
                Cnt++,Sum+=a[Mn].y-a[Mn].x;
                vis[Mn]=1;
            }
            for(int i=0;i<vec[2].size();i++) vis[vec[2][i]]=0;

            if(Cnt==vec[2].size()) ST=mid,r=mid-1;
            else l=mid+1;
        }

        ST=max(ST,sum1);
        for(int i=0;i<vec[2].size();i++) ST+=a[vec[2][i]].x;
        ans=min(ans,max(ST,sum2));

        if(IfSwap) {
            swap(vec[1],vec[2]);
            for(int i=0;i<vec[1].size();i++) swap(a[vec[1][i]].x,a[vec[1][i]].y);
            for(int i=0;i<vec[2].size();i++) swap(a[vec[2][i]].x,a[vec[2][i]].y);
        }
        while(!vec[1].empty()) {
            int h=vec[1].back();
            if(a[h].t==3) vec[1].pop_back();
            else break;
        }
        while(!vec[2].empty()) {
            int h=vec[2].back();
            if(a[h].t==3) vec[2].pop_back();
            else break;
        }
    }
}Sol;

int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d%d%d",&a[i].t,&a[i].x,&a[i].y);
        vec[a[i].t].push_back(i);
    }

    for(int s=0;s<(1<<vec[3].size());s++) {
        for(int i=0;i<vec[3].size();i++) {
            if((s>>i)&1) vec[1].push_back(vec[3][i]);
            else vec[2].push_back(vec[3][i]);
        }
        Sol.Solve();
    }
    printf("%d\n",ans);
    return 0;
}

P3965 TJOI2013 循环格

题意

一个矩阵,我们可以给其中每个格子定一个上下左右的方向

定义矩阵是一个循环格,当且仅当从每个格子出发,不断照着格子的方向移动,一定能回到那个出发的格子

特别的,如果走出边界,会循环到另一端

给出一个 n×mn\times mn×m 的矩阵,问最小修改几个格子能使其变成循环格

其中 n,m≤15n,m\leq 15n,m≤15

赛时思路 以及 题解

一个循环格等价于将矩阵分成若干回路

这等价于所有点的入度和出度均为 111

于是考虑网络流

让每个点选择它接下来走向哪里,原方向的费用为 000 ,其它方向的为 111

跑一遍费用流即可

代码

cpp 复制代码
#include<bits/stdc++.h>
# define Maxn 18
# define inf 2e9+1
using namespace std;
int n,m,st,ed,a[Maxn][Maxn];
int pos[Maxn][Maxn],ans;
int head[2*Maxn*Maxn],tot=2;
int v[]={0,0,1,-1},s[]={1,-1,0,0};
char ch;
struct Node{int to,next,w,cost;}e[(Maxn*Maxn*6)*2];
void add(int u,int v,int w,int cost) {
	e[tot]={v,head[u],w,cost};
	head[u]=tot++;
}
void addedge(int u,int v,int w,int cost) {add(u,v,w,cost),add(v,u,0,-cost);}
int Pos(int i,int j,char ch) {
	if(ch=='L') return pos[i][j-1?j-1:m];
	if(ch=='R') return pos[i][j+1<=m?j+1:1];
	if(ch=='U') return pos[i-1?i-1:n][j];
	if(ch=='D') return pos[i+1<=n?i+1:1][j];
}
bool vis[2*Maxn*Maxn];
int dis[2*Maxn*Maxn],cur[2*Maxn*Maxn];
queue<int> q;
bool spfa() {
	for(int i=1;i<=ed;i++) vis[i]=0,dis[i]=inf,cur[i]=head[i];
	dis[st]=0,vis[st]=1,q.push(st);
	while(!q.empty()) {
		int h=q.front();q.pop(),vis[h]=0;
		for(int i=head[h];i;i=e[i].next) {
			if(e[i].w&&dis[e[i].to]>dis[h]+e[i].cost) {
				dis[e[i].to]=dis[h]+e[i].cost;
				if(!vis[e[i].to]) {
					vis[e[i].to]=1;
					q.push(e[i].to);
				}
			} 
		}
	}return dis[ed]!=inf;
}
bool Vis[2*Maxn*Maxn];
int dfs(int rt,int flow) {
	if(rt==ed) {ans+=dis[rt]*flow;return flow;};
	Vis[rt]=1;
	int res=0;
	for(int i=cur[rt];i;i=e[i].next) {cur[rt]=i;
		if((!Vis[e[i].to])&&e[i].w&&dis[e[i].to]==dis[rt]+e[i].cost) {
			int nowflow=dfs(e[i].to,min(flow,e[i].w));
			if(!nowflow) dis[e[i].to]=inf;
			else {
				e[i].w-=nowflow;
				e[i^1].w+=nowflow;
				flow-=nowflow;
				res+=nowflow;
				if(!flow) {Vis[rt]=0;return res;}
			}	
		}
	}Vis[rt]=0;return res;
}
int main() {
	scanf("%d%d",&n,&m),st=2*n*m+1,ed=2*n*m+2;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) pos[i][j]=(i-1)*m+j;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			cin>>ch;
			addedge(st,pos[i][j],1,0);
			addedge(pos[i][j]+n*m,ed,1,0);
			if(Pos(i,j,'L')!=pos[i][j]) addedge(pos[i][j],Pos(i,j,'L')+n*m,1,ch!='L');
			if(Pos(i,j,'R')!=pos[i][j]) addedge(pos[i][j],Pos(i,j,'R')+n*m,1,ch!='R');
			if(Pos(i,j,'U')!=pos[i][j]) addedge(pos[i][j],Pos(i,j,'U')+n*m,1,ch!='U');
			if(Pos(i,j,'D')!=pos[i][j]) addedge(pos[i][j],Pos(i,j,'D')+n*m,1,ch!='D');
		}
	}
	while(spfa()) dfs(st,inf);
	printf("%d\n",ans);
	return 0;
}
/*
3 4
RRRD
URLL
LRRR
*/

P5232 JSOI2012 智者的考验

题意

对于一个 Rx×RyRx\times RyRx×Ry 的矩阵

有 Rx+RyR_x+R_yRx+Ry 个操作

每个操作将它对应的行或列异或 111

同时这个矩阵有一个目标状态,我们称它为 厄运星厄运星厄运星

给出一个长为 nnn 的操作区间,初始时都是 111

有 mmm 个操作,操作有三个类型

1.x,k\verb!1.! x,k1.x,k

将操作序列的第 xxx 个改成 kkk

2.l,r\verb!2.! l,r2.l,r

问顺着操作序列操作,l,rl,rl,r 的操作区间里会出现几个 厄运星厄运星厄运星

3.l,r,k\verb!3.! l,r,k3.l,r,k

将操作序列的 l,rl,rl,r 改成 kkk

其中 n≤106,m≤120000,Rx≤2,Ry≤3n\leq 10^6,m\leq120000,R_x\leq2,R_y\leq3n≤106,m≤120000,Rx≤2,Ry≤3

赛时思路

考虑利用线段树维护

每段区间我们记录一个对应关系 FFF

用 F(s)F(s)F(s) 表示 sss 通过当前区间后会变成什么

同时也记录 AnsAnsAns ,用 Ans(s)Ans(s)Ans(s) 表示 sss 通过区间会造成几个厄运星

于是区间修改就简单了,因为所有人都是同种操作,判断一下奇偶性即可

查询的话可以查两次,先查 1,l−11,l-11,l−1 获得初始状态,再查 l,rl,rl,r 获得答案

时间复杂度 O(nlogn×2Rx+Ry)O(nlogn\times2^{R_x+R_y})O(nlogn×2Rx+Ry)

题解

这题思路不难,问题在于空间

首先一个优化的点是叶子结点不用存

其次是我们将 存状态存状态存状态 改为 存哪些按钮按了存哪些按钮按了存哪些按钮按了 ,状态数缩到 252^525

我们进一步的还能发现按最后一个相当把前面的都按一遍,于是状态数缩到 242^424

此时空间就足以通过这道题了

代码

cpp 复制代码
#include<bits/stdc++.h>
# define Maxn 1000005
# define Maxm 120005
# define pr pair<int,int>
# define fir first
# define sec second
using namespace std;
int n,m,op,x,l,r,k;
int Rx,Ry,S,Change[10];

struct Seg{
    int lz[Maxn<<2];
    int F[Maxn<<1][1<<4|1];
    int Ans[Maxn<<1][1<<4|1];

    int Get(int s,int x) {
        if(x==Rx+Ry) s^=((1<<(Rx+Ry-1))-1);
        else s^=(1<<(x-1));
        return s;
    }
    int trans(int s) {
        int res=0;
        for(int i=0;i<Rx+Ry-1;i++)
            if((s>>i)&1) res^=Change[i];
        return res;
    }
    int Go(int rt,int l,int r,int s) {
        if(l==r) return Get(s,lz[rt]);
        else return F[rt][s];
    }
    int GetAns(int rt,int l,int r,int s) {
        if(l==r) return (trans(Get(s,lz[rt]))==S);
        else return Ans[rt][s];
    }

    void Tag(int rt,int l,int r,int v) {
        if(l==r) lz[rt]=v;
        else {
            for(int s=0;s<(1<<(Rx+Ry-1));s++) {
                int p=Get(s,v);
                if((r-l+1)%2) {
                    F[rt][s]=p;
                    if(trans(s)==S) Ans[rt][s]=(r-l+1)/2;
                    else if(trans(p)==S) Ans[rt][s]=(r-l+2)/2;
                    else Ans[rt][s]=0;
                }
                else {
                    F[rt][s]=s;
                    if(trans(s)==S) Ans[rt][s]=(r-l+1)/2;
                    else if(trans(p)==S) Ans[rt][s]=(r-l+2)/2;
                    else Ans[rt][s]=0;
                }
            }
            lz[rt]=v;
        }
    }
    void Down(int rt,int l,int r,int mid) {
        if(lz[rt]) {
            Tag(rt<<1,l,mid,lz[rt]);
            Tag(rt<<1|1,mid+1,r,lz[rt]);
            lz[rt]=0;
        }
    }

    void update(int rt,int l,int r,int L,int R,int v) {
        if(L<=l&&r<=R) {
            Tag(rt,l,r,v);
            return ;
        }

        int mid=(l+r)>>1;Down(rt,l,r,mid);
        if(L<=mid) update(rt<<1,l,mid,L,R,v);
        if(R>mid) update(rt<<1|1,mid+1,r,L,R,v);

        for(int s=0;s<(1<<(Rx+Ry-1));s++) {
            F[rt][s]=Go(rt<<1|1,mid+1,r,Go(rt<<1,l,mid,s));
            Ans[rt][s]=GetAns(rt<<1,l,mid,s)+GetAns(rt<<1|1,mid+1,r,Go(rt<<1,l,mid,s));
        }
    }
    pr query(int rt,int l,int r,int L,int R,int s) {
        if(!R) return {s,0};
        if(L<=l&&r<=R) {
            if(l==r) {
                int bz=Get(s,lz[rt]);
                return {bz,(trans(bz)==S)};
            }
            else return {F[rt][s],Ans[rt][s]};
        }
        
        int mid=(l+r)>>1;Down(rt,l,r,mid);
        if(R<=mid) return query(rt<<1,l,mid,L,R,s);
        if(L>mid) return query(rt<<1|1,mid+1,r,L,R,s);
        
        pr res1=query(rt<<1,l,mid,L,R,s);
        pr res2=query(rt<<1|1,mid+1,r,L,R,res1.fir);
        return {res2.fir,res1.sec+res2.sec};
    }
}Seg;

int main() {
    scanf("%d%d",&Rx,&Ry);
    for(int i=1;i<=Rx;i++) {
        for(int j=1;j<=Ry;j++)
        scanf("%d",&x),S=S*2+x;
    }
    for(int i=1;i<Rx+Ry;i++) {
        if(i<=Rx) {
            for(int j=1;j<=Ry;j++) {
                int p=Rx*Ry-((i-1)*Ry+j);
                Change[i-1]^=(1<<p);
            }
        }
        else {
            for(int j=1;j<=Rx;j++) {
                int p=Rx*Ry-((j-1)*Ry+(i-Rx));
                Change[i-1]^=(1<<p);
            }
        }
        // printf("%d ",Change[i-1]);
    }
    // printf("\n");

    scanf("%d%d",&n,&m),Seg.update(1,1,n,1,n,1);
    for(int i=1;i<=m;i++) {
        scanf("%d",&op);
        if(op==0) {
            scanf("%d%d",&x,&k);
            Seg.update(1,1,n,x,x,k);

            // printf("%d\n",Seg.trans(Seg.F[1][0]));
        }
        if(op==1) {
            scanf("%d%d",&l,&r);
            pr res=Seg.query(1,1,n,1,l-1,0);
            printf("%d\n",Seg.query(1,1,n,l,r,res.fir).sec);
        }
        if(op==2) {
            scanf("%d%d%d",&l,&r,&k);
            Seg.update(1,1,n,l,r,k);
        }
    }
    return 0;
}
相关推荐
元启数宇1 小时前
疏散指示AI实战:规范布点与路径推演全流程
人工智能·算法
tg:;1 小时前
Catkin 常用命令
开发语言·c++·算法
暖阳华笺1 小时前
【高频考点】回溯(暴力搜索)
数据结构·c++·算法·回溯法
hunterkkk(c++)1 小时前
学习dijkstra算法(c++)
c++·学习·算法
lightqjx2 小时前
【算法】数据结构_单调队列
数据结构·算法·单调队列
c++之路2 小时前
Linux 下 C++ 开发环境搭建
linux·运维·c++
小四季豆2 小时前
《数据结构与算法》-顺序表:算法落地的第一个线性结构
c语言·数据结构·算法
8Qi82 小时前
LeetCode 96:不同的二叉搜索树(Unique Binary Search Trees)—— 题解 ✅
算法·leetcode·职场和发展·动态规划
189228048612 小时前
NV041固态MT29F16T08GSLCEM9-QBES:C
人工智能·算法·microsoft·缓存·性能优化