春训第二场。
之前实在是太懒了,开学说要好好练,到现在还是几乎没做什么。从这场开始努力!
两点在移动过程中的距离可以算一下,化简后是关于\(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; }
搜索所有情况,要判断是不是每个点都出现了。用哈希的方法很巧妙:给每个点赋一个随机数 \(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; }