noip模拟测试37
考试总结:这次考试,难度不是很大,但是成绩却不太理想。看完题目,我多多少少都有一些思路,T1我看出了 \(ax+by=c\) 的不定方程求解,但我把板子给忘了,搞了快两个小时才搞出一组特解,但是因为特解会有负数,我当时就怀疑思路的正确性,也没仔细想,因为时间比较紧张,就把它弃了,然后是T2,我觉得和就是一道简单dp 加上线段树优化,码完了发现不对,这时候还剩差不多一个小时,我把自己hack之后也没有更正思路,就打了个测试点分治,然后搞T3,我觉得这是个最小生成树,边想边打,打完了发现自己思路不对,应该跑最短路,就打了个暴力的最短路,然后T4,觉得是个并查集,当时觉得根据真假在每个人之间连边,但是没有实现出来,总体来说,这次考试时间把握不够合理,发挥不是很好,同时我的一些知识点掌握不扎实,应该多加复习。
T1 数列
思路:对于一个数,我们要解方程 ax+by=m,并最小化 abs(x)+abs(y)。先用 exgcd
求出一组解(x’,y’),那么解集是(x’+kb,y’-ka),
证明: 若 ax+by=m,则 ax+by+kab-kab=m,移项,合并即可。
显然 x 只会取最小的正值或最大的负值,那么我们就分别计算,取最小即可。
补充芝士:扩展欧几里得,求 \(ax+by=gcd(a,b)\),
\(gcd(a,b)=gcd(b,a\% b)\)
\(ax_1+by_1=gcd(a,b)\)
\(bx_2+(a\%b)y2=gcd(b,a\% b)\)
我们可以将(a%b)写成\((a-\lfloor\frac{a}{b}\rfloor*b)\)
将括号打开常数a,b合并,合并之后的结果为\(a*y_2+b*(x_2-\lfloor\frac{a}{b}\rfloor*y_2)\)
我们将两式子联立,对比系数即可得到\(x_1=y_2\),\(x_1=y_2,y_1=x_2-\lfloor\frac{a}{b}\rfloor*y_2\)
这样就可以求得一组x,y,那么一组满足条件(\(ax+by=c\))的特解 x 即为 \((x*(c/gcd)\%(b/gcd)+(b/gcd))\%(b/gcd)\)
代码如下:
AC_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1e5+10;
int n,a,b,c,x,y;
int ans,gcd;
ii read()
{
int x=0;
bool f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
iv oj(int i,int j)
{
if(j==0)
{
x=1;
y=0;
gcd=i;
return;
}
oj(j,i%j);
int tmp=x;
x=y;
y=tmp-i/j*y;
}
signed main()
{
n=read();
a=read();
b=read();
if(a<b)
swap(a,b);
oj(a,b);
a/=gcd;
b/=gcd;
int x1,y1,x2,y2,out;
bool br=0;
for(re i=1;i<=n;i++)
{
c=read();
if(c%gcd!=0||br)
{
br=1;
continue;
}
c/=gcd;
x1=(x*c+b)%b;
y1=(c-a*x1)/b;
out=abs(x1)+abs(y1);
if(x1<0)
{
x2=x1+((-x1)/b+1)*b;
y2=y1-((-x1)/b+1)*a;
out=min(out,abs(x2)+abs(y2));
x2=x1+((-x1)/b)*b;
y2=y1-((-x1)/b)*a;
out=min(out,abs(x2)+abs(y2));
}
else
{
x2=x1-(x1/b)*b;
y2=y1+(x1/b)*a;
out=min(out,abs(x2)+abs(y2));
x2=x1-(x1/b+1)*b;
y2=y1+(x1/b+1)*a;
out=min(out,abs(x2)+abs(y2));
}
if(y1<0)
{
y2=y1+((-y1)/a+1)*a;
x2=x1-((-y1)/a+1)*b;
out=min(out,abs(x2)+abs(y2));
y2=y1+((-y1)/a)*a;
x2=x1-((-y1)/a)*b;
out=min(out,abs(x2)+abs(y2));
}
else
{
y2=y1-(y1/a)*a;
x2=x1+(y1/a)*b;
out=min(out,abs(x2)+abs(y2));
y2=y1-(y1/a+1)*a;
x2=x1+(y1/a+1)*b;
out=min(out,abs(x2)+abs(y2));
}
ans+=out;
}
if(br)
printf("-1\n");
else
printf("%lld\n",ans);
return 0;
}
T2 数对
这道题与之前的一道考试题比较类似,就是多了一个排序,如果不考虑排序问题,那么对于一个给定的序列S,我们就是要求出满足条件的最大权值和,设\(f_{i,j}\)表示前 i 个数中,最大a值为 j 的最大权值之和,那么考虑转移,因为限制条件同时有a,b, 所以我们根据当前 a与 b 的大小关系来分情况讨论,
- \(a_i<b_i\) 那么\(f_{i,j}=max(f_[i,j],f_{i-1,k}+val[i]) (1<=k<=a_i)\)
\(f_{i,j}=max(f_{i,j},f_{i-1,j}+val_i) ,(a_i+1<=j<=b_i)\)
2.\(a_i>=b_i\) 那么 \(f_{i,j}=max(f_{i,j},f_{i-1,k}+val_i),(1<=k<=b_i)\)
那么现在我们考虑顺序问题,考虑两个数对(a i ,b i )和(a j ,b j ),如果 a i <b j 并且 b i >a j ,那么我们望 i 排在j 前面。对于相反的情况,我们希望 j 排在 i 前面。其余两种情况显然按 a+b 从小到大排列就可以满足所有的需求,
60pts_朴素DP
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1e5+10;
int n,cnt,maxx,sum;
int f[3100][6100],lsh[N<<1];
struct node
{
int x,y,w;
friend bool operator < (node a,node b)
{
if(a.x<b.y and b.x>a.y)
return 1;
else if(a.x>b.y and b.x<a.y)
return 0;
return (a.x+a.y)<(b.x+b.y);
}
}use[N];
ii read()
{
int x=0;
bool f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
signed main()
{
n=read();
int a,b,c;
for(re i=1;i<=n;i++)
{
a=read();
b=read();
c=read();
use[i].x=a;
use[i].y=b;
use[i].w=c;
lsh[++cnt]=a;
lsh[++cnt]=b;
}
sort(use+1,use+n+1);
sort(lsh+1,lsh+cnt+1);
cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
for(re i=1;i<=n;i++)
{
a=use[i].x;
b=use[i].y;
a=lower_bound(lsh+1,lsh+cnt+1,a)-lsh;
b=lower_bound(lsh+1,lsh+cnt+1,b)-lsh;
use[i].x=a;
use[i].y=b;
sum+=use[i].w;
}
f[1][use[1].x]=use[1].w;
for(re i=2;i<=n;i++)
{
for(re j=1;j<=cnt;j++)
f[i][j]=f[i-1][j];
if(use[i].x<=use[i].y)
{
for(re j=1;j<=use[i].x;j++)
f[i][use[i].x]=max(f[i][use[i].x],f[i-1][j]+use[i].w);
for(re j=use[i].x+1;j<=use[i].y;j++)
f[i][j]=max(f[i][j],f[i-1][j]+use[i].w);
}
else
{
for(re j=1;j<=use[i].y;j++)
f[i][use[i].x]=max(f[i][use[i].x],f[i-1][j]+use[i].w);
}
}
for(re i=1;i<=n;i++)
{
for(re j=1;j<=cnt;j++)
maxx=max(maxx,f[i][j]);
}
cout<<maxx<<endl;
return 0;
}
AC_code,线段树优化
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=2e5+10;
int n,cnt,maxx,sum,ans;
int lsh[N<<1];
struct node
{
int x,y,w;
friend bool operator < (node a,node b)
{
if(a.x<b.y and b.x>a.y)
return 1;
else if(a.x>b.y and b.x<a.y)
return 0;
return (a.x+a.y)<(b.x+b.y);
}
}use[N];
ii read()
{
int x=0;
bool f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
struct Segment_Tree
{
int dp[N<<2],lazy[N<<2];
iv pp(int rt)
{
dp[rt]=max(dp[lc],dp[rc]);
}
iv pd(int rt)
{
if(lazy[rt])
{
dp[lc]+=lazy[rt];
dp[rc]+=lazy[rt];
lazy[lc]+=lazy[rt];
lazy[rc]+=lazy[rt];
lazy[rt]=0;
}
}
iv insert(int rt,int l,int r,int p,int z)
{
if(l==r)
{
dp[rt]=max(dp[rt],z);
return;
}
pd(rt);
if(mid>=p)
insert(lc,l,mid,p,z);
else
insert(rc,mid+1,r,p,z);
pp(rt);
}
iv updata(int rt,int l,int r,int L,int R,int z)
{
if(L>R||L>r||R<l)
return;
if(L<=l&&r<=R)
{
dp[rt]+=z;
lazy[rt]+=z;
return;
}
pd(rt);
if(mid>=L)
updata(lc,l,mid,L,R,z);
if(mid<R)
updata(rc,mid+1,r,L,R,z);
pp(rt);
}
ii query(int rt,int l,int r,int L,int R)
{
if(L>R)
return 0;
if(L<=l&&r<=R)
return dp[rt];
pd(rt);
if(mid>=R)
return query(lc,l,mid,L,R);
if(mid<L)
return query(rc,mid+1,r,L,R);
return max(query(lc,l,mid,L,R),query(rc,mid+1,r,L,R));
}
}T;
signed main()
{
n=read();
int a,b,c;
for(re i=1;i<=n;i++)
{
a=read();
b=read();
c=read();
use[i].x=a;
use[i].y=b;
use[i].w=c;
lsh[++cnt]=a;
lsh[++cnt]=b;
}
sort(use+1,use+n+1);
sort(lsh+1,lsh+cnt+1);
cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
for(re i=1;i<=n;i++)
{
a=use[i].x;
b=use[i].y;
a=lower_bound(lsh+1,lsh+cnt+1,a)-lsh;
b=lower_bound(lsh+1,lsh+cnt+1,b)-lsh;
use[i].x=a;
use[i].y=b;
}
for(re i=1;i<=n;i++)
{
ans=0;
if(use[i].x<use[i].y)
{
ans=T.query(1,1,cnt,1,use[i].x);
T.insert(1,1,cnt,use[i].x,ans+use[i].w);
T.updata(1,1,cnt,use[i].x+1,use[i].y,use[i].w);
}
else
{
ans=T.query(1,1,cnt,1,use[i].y);
T.insert(1,1,cnt,use[i].x,ans+use[i].w);
}
}
printf("%lld\n",T.query(1,1,cnt,1,cnt));
return 0;
}
T3 最小距离
思路:这道题说白了就是一个多源最短路,在我们跑单源最短路(Dij)的时候我们刚开始将一个起点压进堆,这道题我们将所有特殊点压进堆里,然后更新出所有的dis数组,结束后我们枚举每条边,如果这条边两端的点不是由同一个源点更新的,那么就更新这两个源点之间的答案。
代码如下:
AC_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
#define next nettrr
#define head hheees
using namespace std;
const int N=2e5+10;
const int INF=1e17;
struct node
{
int s,w;
friend bool operator < (node a,node b)
{
return a.w>b.w;
}
};
struct CUN
{
int st,ed,w;
}use[N];
priority_queue<node> Q;
int n,m,p,tot;
int to[N<<1],next[N<<1],head[N],val[N<<1];
int csp[N],dis[N],be[N],my_min[N];
bool sp[N],vis[N];
ii read()
{
int x=0;
bool f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
iv add(int x,int y,int z)
{
to[++tot]=y;
next[tot]=head[x];
head[x]=tot;
val[tot]=z;
}
ii gett(int x)
{
if(x==be[x])
return x;
return be[x]=gett(be[x]);
}
iv dj()
{
for(re i=1;i<=n;i++)
{
dis[i]=INF;
my_min[i]=INF;
}
for(re i=1;i<=n;i++)
be[i]=i;
for(re i=1;i<=p;i++)
{
dis[csp[i]]=0;
Q.push((node){csp[i],0});
}
while(!Q.empty())
{
int x=Q.top().s;
int y=Q.top().w;
Q.pop();
if(!vis[x])
{
vis[x]=1;
for(re i=head[x];i;i=next[i])
{
int p=to[i];
if(dis[p]>dis[x]+val[i])
{
dis[p]=dis[x]+val[i];
be[p]=gett(x);
Q.push((node){p,dis[p]});
}
}
}
}
for(re i=1;i<=m;i++)
{
int fx=gett(use[i].st),fy=gett(use[i].ed);
if(fx!=fy)
{
my_min[fx]=min(my_min[fx],dis[use[i].st]+dis[use[i].ed]+use[i].w);
my_min[fy]=min(my_min[fy],dis[use[i].st]+dis[use[i].ed]+use[i].w);
}
}
for(re i=1;i<=p;i++)
printf("%lld ",my_min[csp[i]]);
}
signed main()
{
n=read();
m=read();
p=read();
for(re i=1;i<=p;i++)
{
csp[i]=read();
sp[csp[i]]=1;
}
int u,v,w;
for(re i=1;i<=m;i++)
{
u=read();
v=read();
w=read();
use[i].st=u;
use[i].ed=v;
use[i].w=w;
add(u,v,w);
add(v,u,w);
}
dj();
return 0;
}
T4 真相
65pts_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1010;
int n,t;
char s[5];
int js[N],cun[N],h[N];
int pd[N];
ii read()
{
int x=0;
bool f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
inline bool check()
{
for(re i=2;i<=n;i++)
{
if(pd[i-1]==1)
{
if(cun[i-1]==-1)
pd[i]=1;
else
pd[i]=2;
}
else if(pd[i-1]==2)
{
if(cun[i-1]==-1)
pd[i]=2;
else
pd[i]=1;
}
}
if(pd[n]==1)
{
if(cun[n]==-1 and pd[1]==1)
return 1;
else if(cun[n]==-2 and pd[1]==2)
return 1;
return 0;
}
else if(pd[n]==2)
{
if(cun[n]==-1 and pd[1]==1)
return 0;
else if(cun[n]==-2 and pd[1]==2)
return 0;
return 1;
}
}
iv spe_1()
{
int cnt=0;
memset(pd,0,sizeof(pd));
pd[1]=1;
if(check())
{
printf("consistent\n");
return;
}
memset(pd,0,sizeof(pd));
pd[1]=2;
if(check())
printf("consistent\n");
else
printf("inconsistent\n");
return ;
}
inline bool check_2(int x)
{
int cnt=0;
for(re i=1;i<=h[0];i++)
{
int now=h[i];
while(1)
{
int la=now-1;
if(la==0)
la=n;
if(pd[la])
break;
if(pd[now]==1)
{
if(cun[la]==-1)
pd[la]=1;
else
pd[la]=2;
}
else
{
if(cun[la]==-1)
pd[la]=2;
else
pd[la]=1;
}
now=la;
}
}
for(re i=1;i<=n;i++)
if(pd[i]==1)
++cnt;
if(cnt==x)
return 1;
return 0;
}
iv spe_2()
{
bool ke=0;
for(re i=0;i<=n;i++)
{
memset(pd,0,sizeof(pd));
for(re j=1;j<=h[0];j++)
{
if(cun[h[j]]!=i)
pd[h[j]]=2;
else
pd[h[j]]=1;
}
if(check_2(i))
{
ke=1;
break;
}
}
if(ke)
printf("consistent\n");
else
printf("inconsistent\n");
return ;
}
signed main()
{
t=read();
while(t--)
{
n=read();
bool hav=0;
memset(cun,0,sizeof(cun));
memset(h,0,sizeof(h));
for(re i=1;i<=n;i++)
{
scanf("%s",s);
if(s[0]=='$')
{
hav=1;
cun[i]=read();
h[++h[0]]=i;
}
else if(s[0]=='+')
{
cun[i]=-1;
}
else if(s[0]=='-')
{
cun[i]=-2;
}
}
if(!hav)
spe_1();
else
spe_2();
}
return 0;
}