2022春训第二场
之前实在是太懒了,开学说要好好练,到现在还是几乎没做什么。从这场开始努力!
[D]
分析:
两点在移动过程中的距离可以算一下,化简后是关于\(t\)的一次或二次函数(\(a\)>=0)。然后简单判断就可以了;但是一直卡在第21个点过不去。
找了一篇来拍,结果拍到一个点竟然是那篇错了。总之思路挺简单,就摆在这吧。
代码如下
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define db double
db const EPS=1e-10;
int const N=1e5+5,M=1005;
int n,d1,d2;
int dis2(int x1,int y1,int x2,int y2) {return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);}
int geta(int tx1,int ty1,int tx2,int ty2,int x1,int y1,int x2,int y2)
{return ((x1-x2)-(tx1-tx2)) * ((x1-x2)-(tx1-tx2))
+ ((y1-y2)-(ty1-ty2)) * ((y1-y2)-(ty1-ty2));}
int getb(int tx1,int ty1,int tx2,int ty2,int x1,int y1,int x2,int y2)
{return ( 2*(x1-x2)*(tx1-tx2) - 2*(tx1-tx2)*(tx1-tx2) )
+ ( 2*(y1-y2)*(ty1-ty2) - 2*(ty1-ty2)*(ty1-ty2) );}
int getc(int tx1,int ty1,int tx2,int ty2,int x1,int y1,int x2,int y2)
{return (tx1-tx2)*(tx1-tx2) + (ty1-ty2)*(ty1-ty2);}
db cal(int a,int b,int c,db t)
{return (db)a*t*t + (db)b*t + c;}
db getmint(int a,int b,int c)
{
db p = (-0.5) * (db)b / (db)a ;
if(a==0) return (b>=0)?0:1;//
else // a>0
{
if(a*b >= 0) return 0; // p<=0
else if(b <= (-2)*a) return 1; // p>=1
else return p;
}
}
db getmaxt(int a,int b,int c)
{
// db p = (-1.0) * b / (2.0 * a);
if(a==0) return (b>=0)?1:0;//
else // a>0
{
if(a*b >= 0) return 1; // p<=0
else if(b <= (-2)*a) return 0; // p>=1
// else return (cal(a,b,c,0)-cal(a,b,c,1)>EPS)?0:1;
else return (0 > a+b)?0:1;
}
}
int main()
{
scanf("%d%d%d",&n,&d1,&d2);
d1 = d1*d1; d2 = d2*d2; //平方
int x1,y1,x2,y2,tx1,ty1,tx2,ty2;
scanf("%d%d%d%d",&tx1,&ty1,&tx2,&ty2);
int ans=0; bool fl=1; //fl=1:可以
for(int i=2;i<=n;i++)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
int a=geta(tx1,ty1,tx2,ty2,x1,y1,x2,y2);
int b=getb(tx1,ty1,tx2,ty2,x1,y1,x2,y2);
int c=getc(tx1,ty1,tx2,ty2,x1,y1,x2,y2);
printf("i=%d a=%d\n",i,a);
db mint=getmint(a,b,c), maxt=getmaxt(a,b,c);
db mind=cal(a,b,c,mint), maxd=cal(a,b,c,maxt);
// printf("i=%d d1=%d d2=%d maxt=%.2lf mint=%.2lf maxd=%.2lf mind=%.2lf\n",i,d1,d2,maxt,mint,maxd,mind);
if(mind - d1 <= EPS) // mind <= d1
{
if(mint - maxt < EPS) // mint < maxt
{
if(fl || cal(a,b,c,0) - d2 > EPS) ans++, fl = ((maxd - d2) > EPS),
printf("i=%d\n",i);
else fl |= ((maxd - d2) > EPS);
}
else // mint >= maxt
{
if(fl || maxd - d2 > EPS) ans++, fl = ((cal(a,b,c,1) - d2) > EPS),
printf("i=%d\n",i);
// else fl=0;
else fl |= ((maxd - d2) > EPS);
}
}
else fl |= (maxd - d2 > EPS);
tx1 = x1; ty1 = y1; tx2 = x2; ty2 = y2;
}
printf("%d\n",ans);
return 0;
}
[E]
分析:
搜索所有情况,要判断是不是每个点都出现了。用哈希的方法很巧妙:给每个点赋一个随机数 \(b[i]\) ,一种情况的哈希值就是它里面出现的点的 \(b\) 的异或和。
所有点都出现的情况就是所有 \(b[i]\) 的异或和。
没见过的哈希做法,值得记录。
代码如下
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pb push_back
int const bs=137,md=998244353;
int const N=2e5+5,M=10;
int n,m,k,num[M],ans; // num[i]:出度为i的节点的个数
ll tot,b[N]; // 每个点的一个随机数(哈希值)
ll hsh[M][M]; // hsh[i][j]:出度为i的节点都走第j小的边,按顺序得到的去向点的哈希值
vector<ll>can; // n排列的哈希值
struct Ed{
int to,w;
Ed(int t=0,int ww=0){to=t; w=ww;}
bool operator < (const Ed &b) const
{return w < b.w;}
};
struct Nd{
int id,out;
vector<Ed>ed;
bool operator < (const Nd &b) const
{return out < b.out;}
}a[N];
ull Rand()
{
// ull ret=rand();
// ret <<= 10;
// ret ^= 12345;
// return ret*rand();
return rand();
}
void dfs(int t,ll h)
{
if(t<=k)
{
for(int i=1;i<=t;i++)
dfs(t+1,h^hsh[t][i]);
}
else
{
if(h == tot) ans++;
return;
}
}
int main()
{
srand(time(0));
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) a[i].id=i;
for(int i=1,u,v,w;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
a[u].out++; a[u].ed.pb(Ed(v,w));
}
for(int i=1;i<=n;i++)
sort(a[i].ed.begin(), a[i].ed.end()), num[a[i].out]++;
for(int i=1;i<=n;i++) b[i]=Rand(), tot^=b[i];
for(int p=1;p<=n;p++)
{
int t = a[p].out;
for(int j=1;j<=t;j++) // 第j小的边
{
int to = a[p].ed[j-1].to;
hsh[t][j] ^= b[to];
}
}
dfs(1,0);
printf("%d\n",ans);
return 0;
}