【比赛】暑假集训3
题面和题解
A.数列
看到这些操作先不要慌,把它转化成方程形式.
既然只能加减 和 ,那么我们可以列一组方程:
既然我们会用exgcd求方程 的解,那么只需要将其乘以 再除以最大公约数就行了.
那么要最小化 ,可以先使用exgcd求出一组特解(x',y'),解集显然为 .
这样,只需要求x趋近于0的正负两个值并计算出相应的y,求y趋近于0的正负两个值并计算出相应的x,四个值取min即可.
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
using namespace std;
static inline ll read()
{
rll f=0,x=0;rg char ch=getchar();
while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}
static inline void write(rll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);putchar(x%10|48);
}
ll n,g,a,b,x,y;
ll k,ans;
ll t1,t2,t3,t4,t5,k1,k2;
static inline ll gcd(rll a,rll b,rll& x,rll& y)
{
if(!b) { x=1;y=0;return a; }
rll f=gcd(b,a%b,x,y),t=x;
x=y;y=t-a/b*y;return f;
}
static inline ll solve(rll k,rll a,rll b)
{
rll ans;
if(k%g) return -1;
rll p=x*k/g,q=y*k/g,p1;
if(p<0) p=-p,q=-q;
p1=p/b;p-=p1*b;q+=p1*a;
ans=min(abs(p)+abs(q),abs(p-b)+abs(q+a));
if(q<0) p=-p,q=-q;
p1=q/a;p+=p1*b;q-=p1*a;
ans=min(ans,min(abs(p)+abs(q),abs(p+b)+abs(q-a)));
return ans;
}
int main()
{
n=read();a=read();b=read();
g=gcd(a,b,x,y);a/=g;b/=g;
while(n--)
{
k=abs(read());if(!k) continue;
k1=solve(k,a,b);
if(k1==-1) { puts("-1"); return 0; }ans+=k1;
}
write(ans);
return 0;
}
B.数对
考虑两个数对 和 ,如果 并且 ,那么我们希望 排在 前面.对于相反的情况,我们希望 排在 前面.其余两种情况 和 以任意顺序排列都是相同的.显然按 从小到大排列就可以满足所有的需求.
设 表示前 个数中已选数的 为 时最多能选择多少个数.
那么转移好说.
当 时,
当 时,
那么 .
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 400001
using namespace std;
static inline ll read()
{
rll f=0,x=0;rg char ch=getchar();
while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}
static inline void write(rll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);putchar(x%10|48);
}
struct node
{
ll a,b,w;
inline friend bool operator<(rg node a,rg node b)
{
return a.a+a.b<b.a+b.b;
}
}a[maxn];
ll n,len;
ll ans;
ll b[maxn<<1];
ll dp[2][maxn],k;
int main()
{
n=read();
for(rll i=1;i<=n;i++) a[i].a=b[(i<<1)-1]=read(),a[i].b=b[i<<1]=read(),a[i].w=read();
sort(a+1,a+n+1);sort(b+1,b+(n<<1)+1);
len=unique(b+1,b+(n<<1)+1)-b-1;
for(rll i=1;i<=n;i++)
a[i].a=lower_bound(b+1,b+len+1,a[i].a)-b,
a[i].b=lower_bound(b+1,b+len+1,a[i].b)-b;
for(rll i=1;i<=n;i++)
{
rll t=min(a[i].a,a[i].b);
for(rll j=1;j<=len;j++) dp[k][j]=dp[k^1][j];
for(rll j=1;j<=t;j++) dp[k][a[i].a]=max(dp[k][a[i].a],dp[k^1][j]+a[i].w);
ans=max(ans,dp[k][a[i].a]);
for(rll j=a[i].a+1;j<=a[i].b;j++) dp[k][j]=max(dp[k][j],dp[k^1][j]+a[i].w),ans=max(ans,dp[k][j]);
k^=1;
}
write(ans);
return 0;
}
这样做会T(即使用滚动数组优化),考虑将其放入线段树中(因为只有区间加、单点改、区间查询).
于是就有了:
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 400001
using namespace std;
static inline ll read()
{
rll f=0,x=0;rg char ch=getchar();
while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}
static inline void write(rll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);putchar(x%10|48);
}
struct node
{
ll a,b,w;
inline friend bool operator<(rg node a,rg node b)
{
return a.a+a.b<b.a+b.b;
}
}a[maxn];
struct tree
{
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
ll dp,tag;
}t[maxn<<2];
ll n,len;
ll b[maxn<<1];
// ll dp[2][maxn],k;
#define pushup(rt) t[rt].dp=max(t[ls(rt)].dp,t[rs(rt)].dp)
static inline void pushdown(rll rt)
{
if(t[rt].tag)
{
t[ls(rt)].dp+=t[rt].tag;t[rs(rt)].dp+=t[rt].tag;
t[ls(rt)].tag+=t[rt].tag;t[rs(rt)].tag+=t[rt].tag;
t[rt].tag=0;
}
}
static inline void upd_add(rll rt,rll l,rll r,rll x,rll y,rll v)
{
if(x<=l&&r<=y) { t[rt].dp+=v; t[rt].tag+=v; return; }
pushdown(rt);
rll mid=(l+r)>>1;
if(x<=mid) upd_add(ls(rt),l,mid,x,y,v);
if(y>mid) upd_add(rs(rt),mid+1,r,x,y,v);
pushup(rt);
}
static inline void upd_mx(rll rt,rll l,rll r,rll pos,rll v)
{
if(l==r) { t[rt].dp=max(t[rt].dp,v); return; }
pushdown(rt);
rll mid=(l+r)>>1;
if(pos<=mid) upd_mx(ls(rt),l,mid,pos,v);
else upd_mx(rs(rt),mid+1,r,pos,v);
pushup(rt);
}
static inline ll query(rll rt,rll l,rll r,rll x,rll y)
{
if(x>r||y<l) return LLONG_MIN;
if(x<=l&&r<=y) return t[rt].dp;
pushdown(rt);
rll mid=(l+r)>>1;
return max(query(ls(rt),l,mid,x,y),query(rs(rt),mid+1,r,x,y));
}
int main()
{
n=read();
for(rll i=1;i<=n;i++) a[i].a=b[(i<<1)-1]=read(),a[i].b=b[i<<1]=read(),a[i].w=read();
sort(a+1,a+n+1);sort(b+1,b+(n<<1)+1);
len=unique(b+1,b+(n<<1)+1)-b-1;
for(rll i=1;i<=n;i++)
a[i].a=lower_bound(b+1,b+len+1,a[i].a)-b,
a[i].b=lower_bound(b+1,b+len+1,a[i].b)-b;
for(rll i=1;i<=n;i++)
{
rll t=min(a[i].a,a[i].b);
// for(rll j=1;j<=len;j++) dp[k][j]=dp[k^1][j];
// for(rll j=1;j<=t;j++) dp[k][a[i].a]=max(dp[k][a[i].a],dp[k^1][j]+a[i].w);
// ans=max(ans,dp[k][a[i].a]);
rll ans=query(1,1,len,1,t);
upd_mx(1,1,len,a[i].a,ans+a[i].w);
// for(rll j=a[i].a+1;j<=a[i].b;j++) dp[k][j]=max(dp[k][j],dp[k^1][j]+a[i].w),ans=max(ans,dp[k][j]);
if(a[i].a<a[i].b) upd_add(1,1,len,a[i].a+1,a[i].b,a[i].w);
// k^=1;
}
write(query(1,1,len,1,len));
return 0;
}
C.最小距离
一看数据范围显然直接跑最短路会T飞.所以考虑优化一下.
以所有特殊点为起点跑多源最短路,并且记录每个点是由哪个源点拓展的.
然后枚举所有边,如果边的两端是由不同源点拓展的,就更新这两个点的答案.
std题解的证明:
对于源点 ,由 拓展的点 以及与 相邻且不由 拓展的点 ,
如果 的最优路径从 走到了 ,那么走到拓展 的源点是最优的.因此这个做法是正确的.
没什么好说的,记录特殊点、跑多源最短路、记录与其连接的最近点、遍历一遍所有点查询.
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 200001
using namespace std;
static inline ll read()
{
rll f=0,x=0;rg char ch=getchar();
while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}
static inline void write(rll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);putchar(x%10|48);
}
ll n,m,p;
ll x[maxn],to[maxn],v[maxn];
vector<pair<ll,ll> > g[maxn];
ll connect[maxn];
ll dis[maxn];
ll xp[maxn];
ll ans[maxn];
bool fl[maxn];
queue<ll> q;
priority_queue<pair<ll,ll> > que;
static inline ll dij()
{
rll mn=LLONG_MAX,cnt=0;
while(!q.empty()) q.pop();
memset(dis,0x3f,sizeof(dis));
for(rll i=1;i<=p;i++)
dis[xp[i]]=0,connect[xp[i]]=xp[i],que.push((pll) { 0,xp[i] });
while(!que.empty())
{
cnt++;
rll t=que.top().second;que.pop();
for(rll i=0;i<g[t].size();i++)
{
rll to=g[t][i].first;
if(dis[to]>dis[t]+g[t][i].second)
{
dis[to]=dis[t]+g[t][i].second;
connect[to]=connect[t];
que.push((pll) { -dis[to],to });
}
}
}
return mn;
}
int main()
{
memset(ans,0x3f,sizeof(ans));
n=read();m=read();p=read();
for(rll i=1;i<=p;i++) xp[i]=read(),fl[xp[i]]=1;
for(rll i=1;i<=m;i++) x[i]=read(),to[i]=read(),v[i]=read(),g[x[i]].push_back((pll) { to[i],v[i] }),g[to[i]].push_back((pll) { x[i],v[i] });
dij();
for(rll i=1;i<=m;i++)
{
if(connect[x[i]]==connect[to[i]]) continue;
ans[connect[x[i]]]=min(ans[connect[x[i]]],dis[x[i]]+dis[to[i]]+v[i]);
ans[connect[to[i]]]=min(ans[connect[to[i]]],dis[x[i]]+dis[to[i]]+v[i]);
}
for(rll i=1;i<=p;i++) write(ans[xp[i]]),putchar(' ');
return 0;
}
D.真相
这个题非常有意思.
对于不含'$'的情况,有一个小性质:
'-'的个数为奇数,那么一定不合法;
'-'的个数为偶数,那么一定可以使其合法.
具体可以举几个例子试一试.
受此启发,对于含'$'的情况:
对于说'$'的人,如果他的话的真假确定了,那么在从他的上一个说'$'的人之后直到他的所有人的话的真假就全部确定了.而说'$'的的人只能说真话或假话,那么就可以预处理出其说真话和说假话时这一整段说真话和假话的人数.
for(rll i=1;i<=n;i++) op[n+i]=op[i],a[n+i]=a[i];
tmpadd[first+1]=1;tmpminus[first+1]=0;
for(rll i=first+1;i<=first+n;i++)
{
if(op[i]==3)
{
tmpadd[i+1]=1;tmpminus[i+1]=0;sum+=tmpminus[i];
if(a[i]<=n)
fl[a[i]]=1,add[a[i]]+=tmpadd[i],minu[a[i]]+=tmpminus[i];
}
else
{
if(op[i]==1) tmpadd[i+1]=tmpadd[i]+1,tmpminus[i+1]=tmpminus[i];
else tmpadd[i+1]=tmpminus[i]+1,tmpminus[i+1]=tmpadd[i];
}
}
这样之后,就可以枚举每一个说'$'的人,然后进行判断合法了.
完整代码:
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 200001
using namespace std;
static inline ll read()
{
rll f=0,x=0;rg char ch=getchar();
while(ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}
static inline void write(rll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);putchar(x%10|48);
}
ll t,n,k,first,sum;
char s[2];
short op[maxn];
bool fl[maxn];
ll a[maxn];
ll add[maxn],minu[maxn];
ll tmpadd[maxn],tmpminus[maxn];
int main()
{
t=read();
while(t--)
{
memset(add,0,sizeof(add));
memset(minu,0,sizeof(minu));
memset(fl,0,sizeof(fl));
n=read();first=sum=0;
for(rll i=1;i<=n;i++)
{
scanf("%s",s);
switch(s[0])
{
case '+':op[i]=1;break;
case '-':op[i]=2;break;
case '$':if(!first) first=i;a[i]=read();op[i]=3;break;
}
}
if(!first)
{
for(rll i=1;i<=n;i++) if(op[i]==2) first++;
if(first&1) puts("inconsistent");
else puts("consistent");
continue;
}
for(rll i=1;i<=n;i++) op[n+i]=op[i],a[n+i]=a[i];
tmpadd[first+1]=1;tmpminus[first+1]=0;
for(rll i=first+1;i<=first+n;i++)
{
if(op[i]==3)
{
tmpadd[i+1]=1;tmpminus[i+1]=0;sum+=tmpminus[i];
if(a[i]<=n)
fl[a[i]]=1,add[a[i]]+=tmpadd[i],minu[a[i]]+=tmpminus[i];
}
else
{
if(op[i]==1) tmpadd[i+1]=tmpadd[i]+1,tmpminus[i+1]=tmpminus[i];
else tmpadd[i+1]=tmpminus[i]+1,tmpminus[i+1]=tmpadd[i];
}
}
if(!fl[sum]) { puts("consistent"); continue; }
rg bool flag=0;
for(rll i=1;i<=n;i++)
if(fl[i]&&(sum+add[i]-minu[i]==i))
{
puts("consistent"); flag=1; break;
}
if(!flag) puts("inconsistent");
}
return 0;
}
--END--
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/16589718.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!