国庆假期的考试
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;
}
There is a negligible beginning in all great action and thought.

浙公网安备 33010602011771号