123789456ye

已AFO

国庆假期的考试

10.3

爆零祭

T1 红色

  题意:给定\(i,j\leq 10^5\),求\(gcd(i,j) xor lcm(i,j)\)
  题解:注意开long long就行了
  复杂度:\(< O(\log n)\)

#include<bits/stdc++.h>
using namespace std;
inline int gcd(int a,int b)
{
	if(!b)
		return a;
	return gcd(b,a%b);
}
int main()
{
	freopen("a.in","r",stdin),freopen("a.out","w",stdout);
	int a,b;
	scanf("%d%d",&a,&b);
	int gc=gcd(a,b);
	long long tmp=1ll*a*b/gc;
	printf("%lld",tmp xor 1ll*gc);
	return 0;
}

T2 披风

  题意:给定一张带点权有向图。求所有路径上的点权最大值 - 最小值的最大值。\(n\leq 10^5,m\leq 5* 10^5\)
  题解:第一种是缩点+拓扑+记搜处理出每个节点的前面最大最小值,后面最大最小值。
  \(ans=max(premax-lastmin,lastmax-premin)\)
  然而比较难写 考场写炸
  第二种是bfs
  按点权排序,正着bfs一遍再反着一遍,更新所有点的最小值。
  枚举所有点,其中肯定有最大值的点。
  复杂度:\(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
int n,m,edgenum,head[maxn],res[maxn],x[maxn],y[maxn];
struct Edge
{
	int fr,to;
	bool exi;
}eg[maxn*10];
inline void add(int fr,int to,bool exi)
{
	eg[++edgenum]=(Edge){head[fr],to,exi};
	head[fr]=edgenum;
}
queue<int> q;
inline void bfs(int st)
{
	if(!res[st]) res[st]=x[st];
	int to;
	q.push(st);
	while(!q.empty())
	{
		to=q.front();
		q.pop();
		for(int i=head[to];i;i=eg[i].fr)
			if(eg[i].exi&&!res[eg[i].to])
			{
				res[eg[i].to]=x[st];
				q.push(eg[i].to);
			}
	}
	q.push(st);
	while(!q.empty())
	{
		to=q.front();
		q.pop();
		for(int i=head[to];i;i=eg[i].fr)
			if(!eg[i].exi&&!res[eg[i].to])
			{
				res[eg[i].to]=x[st];
				q.push(eg[i].to);
			}
	}
}
inline bool cmp(int a,int b)
{
	return x[a]<x[b];
}
int main()
{
	freopen("b.in","r",stdin),freopen("b.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		scanf("%d",&x[i]),y[i]=i;
	sort(y+1,y+n+1,cmp);
	int xx,yy,ans=0;
	for(int i=1;i<=m;++i)
		scanf("%d%d",&xx,&yy),add(xx,yy,1),add(yy,xx,0);
	for(int i=1;i<=n;++i)
		bfs(y[i]);
	for(int i=1;i<=n;++i)
		ans=max(ans,x[i]-res[i]);
	printf("%d\n",ans);
	return 0;
}

T3 子弹

  题意:n 个人,每个人有两把刷子,可以任意交换,使得每个人的两把刷子的值的差的绝对值不超过 c 。问最小次数。无解输出 -1 。\(n\leq 16\)
  题解:状压。
  设\(f[k]\)表示 k 状态下最大的独立集划分所包含的子集数,状态各位表示这个人是否在这个需要交换的集合里。记搜转移\(f[k]=max(f[k]+f[i xor k])\)其中i为k的子集。
  初始值如果一开始的集合可以自身独立则为 1 ,否则为 -inf 。f[0]=0
  感性理解可得\(ans=n-f[2^n-1]\)
  复杂度:\(O(2^n*n\log n)\)

#include<bits/stdc++.h>
using namespace std;
#define maxn (1<<n)-1
#define inf 0x3f3f3f3f
int f[131072],a[2][17],n,c;
vector<int> tmp;
inline int check(int x)
{
	tmp.clear();
	for(int i=0;i<n;++i)
		if((1<<i)&x) tmp.push_back(a[0][i+1]),tmp.push_back(a[1][i+1]);
	sort(tmp.begin(),tmp.end());
	for(int i=0;i<(int)tmp.size();i+=2)
		if(tmp[i+1]-tmp[i]>c) return -inf;
	return 1;
}
inline void dfs(int x)
{
	if(f[x]||(!x)) return;
	f[x]=check(x);
	for(int i=x;i;i=(i-1)&x)
	{
		dfs(i);
		dfs(x xor i);
		f[x]=max(f[x],f[i]+f[x xor i]);
	}
}
int main()
{
	freopen("c.in","r",stdin),freopen("c.out","w",stdout);
	scanf("%d%d",&n,&c);
	for(int i=1;i<=n;++i)
		scanf("%d%d",&a[0][i],&a[1][i]);
	dfs(maxn);
	printf("%d\n",n-f[maxn]);
	return 0;
}

T4 侠

  题意:区间求和,区间修改。修改指\(a[i]+=fib[i-l],l\leq i\leq r\),fib为斐波那契数列。fib[0]=0。\(1\leq n,m\leq 10^5\)
  题解:考虑把tag设为初始两项。这样满足可加性。
  设\(F[0]=a,F[1]=b,F[i]=F[i-1]+F[i-2]\)
  则\(F[i]=fib[i-1]* a+fib[i]* b,sumF[i]=(fib[i+1]-1)*a+(fib[i+2]-1)*b\)
  代码写起来比较毒瘤
  复杂度:\(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
#define wmt 1,n,1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=100010;
const int mo=1000000007;
int n,m,z[maxn<<2|1],col[maxn<<2|1][2];
struct rec
{
	int a,b;
	rec(){}
	rec(int a_,int b_)
		{
			a=a_;if(a>=mo) a-=mo;
			b=b_;if(b>=mo) b-=mo;
		}
}f[maxn],sum[maxn];
rec operator + (const rec &a,const rec &b)
{
	return rec(a.a+b.a,a.b+b.b);
}
int operator * (const rec &a,const rec &b)
{
	return (1ll*a.a*b.a+1ll*a.b*b.b)%mo;
}
void update(int rt)
{
	z[rt]=z[rt<<1]+z[rt<<1|1];
	if(z[rt]>=mo) z[rt]-=mo;
}
void color(int l,int r,int rt,int a,int b)
{
	col[rt][0]+=a;if(col[rt][0]>=mo) col[rt][0]-=mo;
	col[rt][1]+=b;if(col[rt][1]>=mo) col[rt][1]-=mo;
	z[rt]+= rec(a,b)*sum[r-l];if(z[rt]>=mo) z[rt]-=mo;
}
void push_col(int l,int r,int rt)
{
	if (col[rt][0]||col[rt][1])
	{
		int m=(l+r)>>1;
		color(l,m,rt<<1,col[rt][0],col[rt][1]);
		int a=rec(col[rt][0],col[rt][1])*f[m+1-l];
		int b=rec(col[rt][0],col[rt][1])*f[m+2-l];
		color(m+1,r,rt<<1|1,a,b);
		col[rt][0]=0;
		col[rt][1]=0;
	}
}
void build(int l,int r,int rt)
{
	if (l==r)
	{
		scanf("%d",&z[rt]);
		return;
	}
	int m=(l+r)>>1;
	build(lson);
	build(rson);
	update(rt);
}
void modify(int l,int r,int rt,int nowl,int nowr,int a,int b)
{
	if (nowl<=l&&r<=nowr)
	{
		int a_=rec(a,b)*f[l-nowl];
		int b_=rec(a,b)*f[l+1-nowl];
		color(l,r,rt,a_,b_);
		return;
	}
	push_col(l,r,rt);
	int m=(l+r)>>1;
	if(nowl<=m) modify(lson,nowl,nowr,a,b);
	if(m<nowr) modify(rson,nowl,nowr,a,b);
	update(rt);
}
int query(int l,int r,int rt,int nowl,int nowr)
{
	if(nowl<=l&&r<=nowr) return z[rt];
	push_col(l,r,rt);
	int m=(l+r)>>1;
	int ans=0;
	if(nowl<=m) ans=query(lson,nowl,nowr);
	if(m<nowr) ans+=query(rson,nowl,nowr);
	if(ans>=mo) ans-=mo;
	return ans;
}
int main()
{
	freopen("d.in","r",stdin);freopen("d.out","w",stdout);
	f[0]=rec(1,0);
	f[1]=rec(0,1);
	for(int i=2;i<maxn;++i)
		f[i] = f[i-1]+f[i-2];
	sum[0]=f[0];
	for(int i=1;i<maxn;++i)
		sum[i]=sum[i-1]+f[i];
	scanf("%d%d",&n,&m);
	build(wmt);
	for(int i=1;i<=m;++i)
	{
		int opt,l,r;
		scanf("%d%d%d",&opt,&l,&r);
		if(opt==1) printf("%d\n",query(wmt,l,r));
		else
		{
			int x;
			scanf("%d",&x);
			modify(wmt,l,r,f[x].b,f[x+1].b);
		}
	}
	return 0;
}

10.4

T1 seat

  题意:有坐标为\((1,1)\sim (n,m)\)的点,前门坐标为\((0,0)\)有 k 个人,后门坐标为\((n+1,0)\),有 (n*m-k) 个人。每个人都只能坐到离他的曼哈顿距离\(\leq s_{i}\)的座位上。给定\(s_{i}\),求是否有合法方案。\(n* m\leq 10^5\)
  题解:将座位按照后门距离排序,前门每个人先坐到恰好的位置上,如果后门的人都坐得了就合法否则不合法。
  复杂度:\(O(n*m\log (n*m))\)

#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
struct Seat
{
	int a,b;
	friend inline bool operator < (Seat a,Seat b)
		{
			return a.b<b.b;
		}
}seat[maxn];
int a[maxn],vis[maxn];
multiset<int> s;
multiset<int>::iterator it;
int main()
{
	freopen("seat.in","r",stdin),freopen("seat.out","w",stdout);
	int n,m,k,tp=0,tmp;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			seat[++tp]=(Seat){i+j,m+1+i-j};
	for(int i=1;i<=k;++i)
	{
		scanf("%d",&tmp);
		s.insert(tmp);
	}
	sort(seat+1,seat+tp+1);
	for(int i=tp;i;--i)
	{
		if(s.empty()) break;
		it=s.lower_bound(seat[i].a);
		if(it==s.end()) continue;
		vis[i]=1;
		s.erase(it);
	}
	if(!s.empty())
	{
		puts("NO");
		return 0;
	}
	scanf("%d",&k);
	for(int i=1;i<=k;++i)
		scanf("%d",&a[i]);
	sort(a+1,a+k+1);
	for(int i=1,j=1;i<=k;++i)
	{
		while(vis[j]) ++j;
		if(seat[j].b>a[i])
		{
			puts("NO");
			return 0;
		}
		++j;
	}
	puts("YES");
	return 0;
}

T2 block

  题意:有一个消方块游戏,每次可以消掉连续颜色相同的一段,得到长度平方的分数。给定长度为 n 序列,求最大分数。\(n\leq 100\)
  题解:设\(f[i][j][k]\)表示从 i 到 j 且后面还有 k 个与 j 颜色相同的块。
  转移的话,要在\(i\leq j\)间找到一个 p 使得\(p\sim j\)颜色相同。在找一个 q 使得 q 与 j 颜色相同且 q 与后面的那一个块颜色不同然后消掉。要用记搜。
  复杂度:\(O(n^4)\) 然而跑不满

#include<bits/stdc++.h>
using namespace std;
#define maxn 105
int a[maxn],f[maxn][maxn][maxn];
inline int solve(int l,int r,int k)
{
	if(l>r) return 0;
	if(f[l][r][k]) return f[l][r][k];
	int p=r;
	while(p>=l&&a[p]==a[r]) --p;
	++p;
	f[l][r][k]=solve(l,p-1,0)+(r-p+1+k)*(r-p+1+k);
	for(int i=p-1;i>=l;--i)
		if(a[i]==a[r]&&a[i]!=a[i+1])
			f[l][r][k]=max(f[l][r][k],solve(l,i,r-p+1+k)+solve(i+1,p-1,0));
	return f[l][r][k];
}
int main()
{
	freopen("block.in","r",stdin),freopen("block.out","w",stdout);
	int t,n;
	scanf("%d",&t);
	for(int i=1;i<=t;++i)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
			scanf("%d",&a[i]);
		printf("%d\n",solve(1,n,0));
		memset(f,0,sizeof(f));
	}
	return 0;
}

T3 return

  题意:似乎以前见过原题???
  给定无向图,每条边边权为 1 ,危险系数为\(w_{i}\),且有 k 个限制,每个限制为不能依次连续走过\(x_{i},y_{i},z_{i}\),且总路程不能超过在以上限制下的最短路路程加上 L 。求路径最大危险系数的最小值。\(n\leq 500,m\leq 10^5,w_{i}\leq 5* 10^6,k \leq 10^5\)
  题解:一种显然的做法是保存前驱然后直接跑spfa。
  另一种则是设\(dis[i][j]\)表示从 i 转移到 j 的最短路然后跑spfa 并没有本质区别
  复杂度:\(O(spfa* \log maxw)\)

#include<bits/stdc++.h>
using namespace std;
#define maxn 505
struct Lim
{
	int x,y,z;
	friend inline bool operator < (Lim a,Lim b)
		{
			if(a.x==b.x)
			{
				if(a.y==b.y) return a.z<b.z;
				return a.y<b.y;
			}
			return a.x<b.x;
		}
	Lim(){}
	Lim(int a,int b,int c):x(a),y(b),z(c){}
};
struct Point
{
	int id,pre;
	Point(){}
	Point(int a,int b):id(a),pre(b){}
};
struct Edge
{
	int fr,to,val;
	Edge(){}
	Edge(int a,int b,int c):fr(a),to(b),val(c){}
}eg[maxn*40];
map<Lim,bool> ma;
queue<Point> q;
int edgenum,head[maxn],dis[maxn],vis[maxn];
int n,m,k,L;
inline void add(int fr,int to,int val)
{
	eg[++edgenum]=Edge(head[fr],to,val);
	head[fr]=edgenum;
}
inline bool spfa(int lim)
{
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0,vis[1]=1;
	q.push(Point(1,0));
	int to,id,pre;
	while(!q.empty())
	{
		id=q.front().id,pre=q.front().pre;
		q.pop();
		vis[id]=0;
		for(int i=head[id];i;i=eg[i].fr)
		{
			to=eg[i].to;
			if(ma[Lim(pre,id,to)]) continue;
			if(eg[i].val>lim) continue;
			if(dis[to]>dis[id]+1)
			{
				dis[to]=dis[id]+1;
				if(!vis[to]) q.push(Point(to,id));
			}
		}
	}
	return dis[n]<=L;
}
int main()
{
	freopen("return.in","r",stdin),freopen("return.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&k,&L);
	int x,y,z,l=0,r=0,mid;
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d%d",&x,&y,&z);
		r=max(r,z);
		add(x,y,z),add(y,x,z);
	}
	for(int i=1;i<=k;++i)
	{
		scanf("%d%d%d",&x,&y,&z);
		ma[Lim(x,y,z)]=true;
	}
	spfa(0x3f3f3f3f);
	L+=dis[n];
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(!spfa(mid)) l=mid+1;
		else r=mid-1;
	}
	printf("%d\n",l);
	return 0;
}
posted @ 2019-10-04 20:16  123789456ye  阅读(7)  评论(0)    收藏  举报