# | 题目 |
---|---|
A | 【NOIP2018模拟赛19.8.20】妹子 |
B | 【NOIP2018模拟赛19.8.20】老大 |
C | 【NOIP2018模拟赛19.8.20】旅程 |
这题坑点很多,,,第一遍打代码完全没动脑子,直接比较长宽,做完之后总感觉不对劲 ,想了想发现可以翻转放置,,,结果,,居然还可以斜着放!!
打了一个不像正解的代码,枚举矩形旋转的角度,然后用三角函数的知识求出小矩形的边在大矩形上的映射,在比较长度就可以了
#include
#define pai 3.1415926
using namespace std;
int main(){
// freopen("girls.in","r",stdin);
// freopen("girls.out","w",stdout);
int t;
scanf("%d",&t);
while(t--){
int a1,b1,a2,b2;
scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
if((a1<=a2&&b1<=b2)||(b2<=b1&&a2<=a1)||(a1<=b2&&b1<=a2)||(a2<=b1&&b2<=a1))printf("Yes");
else{
if(a1*b1>a2*b2)swap(a1,a2),swap(b1,b2);
int flag=0;
for(double i=0;i<=90;i+=0.01){
if(b1*sin(i*pai/180)+a1*cos(i*pai/180)<=a2&&b1*cos(i*pai/180)+a1*sin(i*pai/180)<=b2){printf("Yes");flag=1;break;
}
}
if(!flag)printf("No");
}
if(t)printf("\n");
}
return 0;
}
考试时想的是先找整棵树的重心,然后在重心的每棵子树分别找重心,,但时间不够没打完,其实这个做法的正确性我也完全没证明,不过还是拿了30分
正解有个结论是:奖杯一定会放在整棵树的直径上,用反证法证明一下就好了。
而奖杯要覆盖所有点前提就是一定要覆盖直径的两个端点,于是二分枚举端点到奖杯的距离,check函数就检查两个奖杯之间的子树是否符合要求就可以了,因为端点到奖杯之间的子树一定是符合条件的,依然可以用反证法,如果这之间用一个子节点到奖杯的距离大于端点到奖杯的距离,那这个节点到另一个直径端点的距离就大于现在的直径了,显然不可能
#include
#define inf 99999999
using namespace std;
int n,s,t;
struct node{
int e,n;
}pr[1001000];
int f[1001000],in[1001000],head[1001000],idx,dep[1001000],son[1001000];
void add(int u,int v){
pr[++idx].e=v;
pr[idx].n=head[u];
head[u]=idx;
}
int read(){
char ch;
int flag=1;
ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')flag=-1;
ch=getchar();
}
int sum=0;
while(ch>='0'&&ch<='9'){
sum=sum*10+ch-'0';
ch=getchar();
}
return flag*sum;
}
void dfs1(int x,int fa){
f[x]=fa;
for(int i=head[x];i;i=pr[i].n){
int y=pr[i].e;
if(y==fa)continue;
dep[y]=dep[x]+1;
dfs1(y,x);
}
}
int dis[1001000];
void dfs2(int x){
dis[x]=dep[x];
for(int i=head[x];i;i=pr[i].n){
int y=pr[i].e;
if(y==f[x]||in[y])continue;
dfs2(y);
dis[x]=max(dis[x],dis[y]);
}
}
bool check(int mid){
int a=s,b=t;
while(a!=t){
if(dep[a]-dep[s]==mid)break;
a=son[a];
}
while(b!=s){
if(dep[t]-dep[b]==mid)break;
b=f[b];
}
if(dep[b]-dep[a]>2*mid)return 0;
int ll=a,rr=b;
while(a!=b){
dfs2(b);
int d=dis[b]-dep[b];
if(min(dep[b]-dep[ll],dep[rr]-dep[b])+d>mid)return 0;
b=f[b];
}
dfs2(b);
int d=dis[b]-dep[b];
if(min(dep[b]-dep[ll],dep[rr]-dep[b])+d>mid)return 0;
return 1;
}
int main(){
// freopen("ob.in","r",stdin);
// freopen("ob.out","w",stdout);
n=read();
for(int i=1;idep[s])s=i;
memset(dep,0,sizeof(dep));
dfs1(s,0);
for(int i=1;i<=n;i++)if(dep[i]>dep[t])t=i;
int now=t;
while(now){
in[now]=1;
son[f[now]]=now;
now=f[now];
}
in[s]=in[t]=1;
int l=0,r=dep[t],ans;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))r=mid-1,ans=mid;
else l=mid+1;
}
printf("%d",ans);
return 0;
}
考场上我直接每次硬核删边然后跑spfa,只有40
正解思路很巧妙,转换解题思路,把删边操作转换为加边,这样我们就可以每次用floyed维护,有个细节是因为可以重复删边,所以我们每次要记录第一次删边操作是哪一步,只有读取到记录的这一步才能加边
#include
#define inf 1e15
#define ll long long
using namespace std;
int n,m,top,v[1001][1001];
ll d[1001][1001],ans[100100],a[1001][1001];
struct node{
int c,x,y;
}pr[1001000];
int main(){
// freopen("journey.in","r",stdin);
// freopen("journey.out","w",stdout);
memset(a,127,sizeof(a));
memset(v,127,sizeof(v));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)scanf("%lld",&a[i][j]),d[i][j]=a[i][j];
}
for(int i=1;i<=n;i++)a[i][i]=d[i][i]=0;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&pr[i].c,&pr[i].x,&pr[i].y);
if(pr[i].c==1){
a[pr[i].x][pr[i].y]=inf;
v[pr[i].x][pr[i].y]=min(v[pr[i].x][pr[i].y],i);
}
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j&&i!=k&&k!=j)a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
for(int i=m;i>=1;i--){
if(pr[i].c==2)ans[++top]=a[pr[i].x][pr[i].y];
else{
int x=pr[i].x,y=pr[i].y;
if(v[x][y]!=i)continue;
a[x][y]=min(a[x][y],d[x][y]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)a[i][j]=min(a[i][j],a[i][x]+a[x][y]+a[y][j]);
}
}
for(int i=top;i>=1;i--){
printf("%lld",ans[i]);
if(i>1)printf("\n");
}
return 0;
}