2013多校第三场
hdu 4629
题意:给你n个三角形,问覆盖1~n次的面积各是多少,n < 50;
分析:取出所有端点和交点的x坐标,排序,然后对于每一段xi~xi+1的范围的线段都是不相交的,所以组成的
面积要么是三角形,要么是梯形,可以直接用公式算面积,然后对于每一个三角形的线段都标记该段对于
从下往上的扫描线来说是入边还是出边,然后就可以直接计算出这块面积被覆盖了几次;如入边加1,出边减一
下图,黄色表示覆盖次数;
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 #include<vector> 7 #include<cstdlib> 8 #define pbk push_back 9 using namespace std; 10 const int N = 25050+10; 11 const double eps = 1e-10; 12 inline double sqr(double x){ 13 return x * x; 14 } 15 inline int dcmp(double x){ 16 return x < -eps ? -1 : x > eps; 17 } 18 struct Point{ 19 double x,y; 20 int kind; 21 Point(){} 22 Point(double x,double y,int kind = 0):x(x),y(y),kind(kind){} 23 bool operator < (const Point &p)const{ 24 return dcmp(x - p.x) < 0 || ( dcmp(x - p.x) == 0 && dcmp(y - p.y) < 0 ); 25 } 26 Point operator - (const Point &p)const{ 27 return Point(x - p.x, y - p.y); 28 } 29 Point operator + (const Point &p)const{ 30 return Point(x + p.x, y + p.y); 31 } 32 Point operator * (const double &k)const{ 33 return Point (x*k , y*k); 34 } 35 Point operator / (const double &k)const{ 36 return Point (x/k, y/k); 37 } 38 double operator * (const Point &p)const{ 39 return x * p.y - y * p.x; 40 } 41 double operator / (const Point &p)const{ 42 return x * p.x + y * p.y; 43 } 44 void input(){ 45 scanf("%lf%lf",&x,&y); 46 } 47 void ot(){ 48 printf("%lf %lf\n",x,y); 49 } 50 }; 51 struct Line{ 52 Point a,b; 53 int kind; 54 Line (){} 55 Line (Point a,Point b,int kind = 0):a(a),b(b),kind(kind){} 56 double operator * (const Point &p)const{ 57 return ( b - a ) * ( p - a ); 58 } 59 double operator / (const Point &p)const{ 60 return ( p - a) / ( p - b); 61 } 62 bool parallel(const Line &v){ 63 return !dcmp( ( b - a ) * ( v.b - v.a ) ); 64 } 65 int LineCrossLine(const Line &v){ 66 if ( (*this).parallel(v) ){ 67 return ( dcmp( v * a ) == 0); 68 }return 2; 69 } 70 int SegCrossSeg(const Line &v){ 71 int d1 = dcmp( (*this) * v.a); 72 int d2 = dcmp( (*this) * v.b); 73 int d3 = dcmp( v * a); 74 int d4 = dcmp( v * b); 75 if ( ( d1 ^ d2 ) == -2 && ( d3 ^ d4 ) == -2 ) return 2; 76 return ( ( d1 == 0 && dcmp( (*this) / v.a ) <= 0 ) 77 || ( d2 == 0 && dcmp( (*this) / v.b ) <= 0 ) 78 || ( d3 == 0 && dcmp( v / a ) <= 0 ) 79 || ( d4 == 0 && dcmp( v / b ) <= 0 ) 80 ); 81 } 82 Point CrossPoint(const Line &v){ 83 double s1 = v * a, s2 = v * b; 84 return ( a * s2 - b * s1) / (s2 - s1); 85 } 86 void input(){ 87 a.input(); b.input(); 88 } 89 void ot(){ 90 a.ot(); b.ot(); 91 } 92 93 }; 94 95 int n,poly_n,xn; 96 vector<double> lx; 97 vector<Line> line; 98 double ans[N]; 99 void init(){ 100 int sz = line.size(); 101 for (int i = 0; i < sz; i++){ 102 for (int j = i+1; j < sz; j++){ 103 if (line[i].SegCrossSeg(line[j]) == 2){ 104 Point p = line[i].CrossPoint(line[j]); 105 lx.pbk(p.x); 106 } 107 } 108 } 109 110 sort(lx.begin(),lx.end()); 111 xn = unique(lx.begin(),lx.end()) - lx.begin(); 112 } 113 vector<Point> qu[N]; 114 void work(){ 115 for (int i = 0; i <= n; i++) ans[i] = 0; 116 for (int i = 0; i < xn-1; i++){ 117 int k = 0; 118 for (int j = 0; j+1 < qu[i].size(); j++){ 119 k += qu[i][j].kind; 120 ans[ k ] += (lx[i+1] - lx[i]) * (qu[i][j+1].x+qu[i][j+1].y - qu[i][j].x - qu[i][j].y) / 2; 121 } 122 } 123 for (int i = 1; i <= n; i++) printf("%.10lf\n",ans[i]); 124 } 125 void check(){ 126 for (int i = 0; i < xn - 1; i++){ 127 cout<<qu[i].size()<<" >.<"<<endl; 128 for (int j = 0; j < qu[i].size(); j++){ 129 qu[i][j].ot(); cout<<qu[i][j].kind<<endl; 130 } 131 } 132 } 133 void solve(){ 134 for (int i = 0; i < xn; i++) qu[i].clear(); 135 for (int i = 0; i < line.size(); i++){ 136 int j = lower_bound(lx.begin(),lx.begin()+xn,line[i].a.x) - lx.begin(); 137 for (; j+1 < xn; j++ ){ 138 double l = lx[j], r = lx[j+1]; 139 if (dcmp(r - line[i].b.x) > 0) break; 140 Point p1 = line[i].CrossPoint(Line(Point(l,0), Point(l,1))); 141 Point p2 = line[i].CrossPoint(Line(Point(r,0), Point(r,1))); 142 qu[j].pbk(Point(p1.y, p2.y,line[i].kind)); 143 } 144 } 145 for (int i = 0; i < xn - 1; i++) sort(qu[i].begin(), qu[i].end()); 146 // check(); 147 work(); 148 } 149 int main(){ 150 int T; scanf("%d",&T); 151 while (T--){ 152 scanf("%d",&n); 153 lx.clear(); line.clear();; 154 for (int i = 0; i < n ;i++){ 155 Point t[4]; 156 for (int j = 0; j < 3; j++ ){ 157 t[j].input(); 158 } 159 t[3] = t[0]; 160 int flag = 1; 161 if (dcmp( (t[1] - t[0])*(t[2] - t[0]) ) == 0) flag = 0; 162 163 for (int i = 0; i < 3 && flag; i++ ){ 164 lx.pbk(t[i].x); 165 for (int j = i+1; j < 3; j++){ 166 Line tmp; tmp.a = t[i]; tmp.b = t[j]; 167 if (dcmp( tmp.a.x - tmp.b.x ) > 0) swap(tmp.a, tmp.b); 168 169 Line tmp2 = Line(t[3-i-j], Point(t[3-i-j].x, t[3-i-j].y - 1)); 170 if (tmp.LineCrossLine(tmp2) != 2) continue; 171 Point tp = tmp.CrossPoint(tmp2); 172 if (dcmp(tp.y - t[3-i-j].y) < 0) tmp.kind = 1; 173 else tmp.kind = -1; 174 line.pbk(tmp); 175 } 176 } 177 } 178 init(); 179 solve(); 180 } 181 return 0; 182 }
hdu 4622
题意:给你一个长n的串,Q次询问其子串s[l,r]中不同的串的个数;
分析:正解后缀自动机,或者HASH,比赛的时候用后缀数组水过了,时间复杂度是O(Qn+nlogn);
先对整个串跑一遍后缀数组,然后对于每一次的询问,直接从SA数组里取出来,然后按照统计串中不同字串的论文题来做,需要注意一下不同之处,
当前字串要统计的个数不一定是减去前一个字串统计的数目,也许还要减去更前一个字串的个数;
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<vector> 5 #include<algorithm> 6 #include<cmath> 7 #include<iostream> 8 using namespace std; 9 const int N = 2000+10; 10 struct Suffix_Array{ 11 int a1[N],a2[N],c[N],sa[N],SA[N],*x,*y,n,m; 12 int height[N],*rank; 13 void sort(){ 14 for (int i = 0; i < m ; i++) c[i] = 0; 15 for (int i = 0; i < n; i++) c[x[i]] ++; 16 for (int i = 0; i < m; i++) c[i+1] += c[i]; 17 for (int i = n-1; i>=0; i-- ) SA[--c[x[sa[i]]]] = sa[i]; 18 } 19 void build_SA(char *s){ 20 n = strlen(s); m = 256; 21 x = a1; y = a2; x[n] = y[n] = -1; 22 for (int i = 0; i < n; i++) x[i] = s[i], sa[i] = i; 23 sort(); 24 for (int k = 1; k <= n; k <<= 1){ 25 int p = 0; 26 for (int i = n-k; i < n; i++) sa[p++] = i; 27 for (int i = 0; i < n; i++) if (SA[i] >= k) sa[p++] = SA[i] - k; 28 sort(); 29 30 p = 0; y[ SA[0] ] = 0; 31 for (int i = 1; i < n; i++){ 32 if ( x[SA[i-1]] != x[SA[i]] || x[SA[i-1]+k]!= x[SA[i]+k] ) p++; 33 y[SA[i]] = p; 34 } 35 swap(x,y); 36 if (p+1 == n) break; 37 m = p+1; 38 } 39 rank = x; getHeight(s); 40 } 41 void getHeight(char *s){ 42 int k = 0; 43 for (int i = 0; i < n; i++){ 44 if (k) k--; 45 if (rank[i] == 0) continue; 46 int j = SA[rank[i] - 1]; 47 while (s[j+k] && s[i+k] == s[j+k]) k++; 48 height[rank[i]] = k; 49 } 50 height[n] = 0; 51 } 52 53 }H; 54 int f[12][N]; 55 void initRMQ(int n,int height[]){ 56 n--; 57 for (int i = 1; i <= n; i++) f[0][i] = height[i]; 58 for (int j = 1; (1<<j) <= n; j++) 59 for (int i = 1; i+(1<<(j-1)) <= n; i++){ 60 f[j][i] = min(f[j-1][i] ,f[j-1][i+(1<<(j-1))]); 61 } 62 }; 63 int lcp(int a,int b){ 64 if (a > b) swap(a,b); 65 a++; 66 int k = 0; 67 while (1<<(1+k) <= b - a +1) k++; 68 return min(f[k][a],f[k][b-(1<<k)+1]); 69 } 70 char s[N]; 71 int Q; 72 vector<int> q; 73 void solve(int l,int r){ 74 int n = strlen(s); 75 q.clear(); 76 int cnt = r - l + 1; 77 for (int i = 0; i <n; i++){ 78 if (H.SA[i]>=l && H.SA[i]<=r){ 79 q.push_back(i); 80 cnt -- ; 81 } 82 if (cnt == 0) break; 83 } 84 int ret = r - H.SA[ q[0] ] + 1; 85 int tmp = ret; 86 for (int i = 1; i < q.size(); i++){ 87 int t1 = r - H.SA[ q[i-1] ] + 1; 88 int t2 = r - H.SA[ q[i] ] + 1; 89 int lc = lcp(q[i-1],q[i]); 90 if (lc<tmp) tmp = lc; 91 if (t2 - tmp > 0) ret += t2 - tmp; 92 if (t2 > tmp) tmp = t2; 93 } 94 printf("%d\n",ret); 95 } 96 int main(){ 97 //freopen("D:\\in.txt","r",stdin); 98 //freopen("D:\\out.txt","w",stdout); 99 int T; scanf("%d",&T); 100 while (T--){ 101 scanf("%s",s); 102 scanf("%d",&Q); 103 H.build_SA(s); 104 //check(); 105 initRMQ(H.n,H.height); 106 while (Q--){ 107 int l,r; 108 scanf("%d%d",&l,&r); 109 solve(l-1,r-1); 110 } 111 } 112 return 0; 113 }
hdu 4627
题意:给你一个数n,求a+b = n,lcm(a,b)最大;
分析:n为奇数,答案(n/2)*(n/2+1);
n为偶数,k = n/2;k是奇数,答案为k-2,k+2;否则k-1,k+1;比赛的时候直接暴力找了。
1 #include<cstring> 2 #include<cstdlib> 3 #include<iostream> 4 #include<cmath> 5 #include<vector> 6 #include<algorithm> 7 #include<cstdio> 8 using namespace std; 9 typedef long long LL; 10 const int N = 1000; 11 LL n; 12 void solve(){ 13 LL l ,r; 14 if (n%2 == 0){ 15 l = r =n/2; 16 }else { 17 l = n/2; r = n/2+1; 18 } 19 LL ans = 0; 20 while (1){ 21 LL t = __gcd(l,r); 22 if (t==1){ 23 if (l*r>ans) ans = l*r; 24 break; 25 }else { 26 if (l*r/t > ans) ans = l*r/t; 27 } 28 l--; r++; 29 if (l == 0) break; 30 } 31 printf("%I64d\n",ans); 32 33 } 34 int main(){ 35 int T; scanf("%d",&T); 36 while (T--){ 37 scanf("%I64d",&n); 38 solve(); 39 } 40 return 0; 41 }
hdu 4628
题意:给你一个串,每次只能删除一个回文序列,求最少多少次能把这个串删完;
分析:n = 16,状压,预处理出所有是回文序列的情况,然后DP也过了,题解是一个3^n的方法,就是每次用位运算每次枚举I的子集;
这个是枚举i的子集的位运算写法:for (int j = i; j ; j = i & (j-1));
然后为什么是3^n次,首先从0~1<<n里面含有一个1的个数是c(n,1),2个1的有c(n,2),...n个1的c(n,n);
含有一个1的数的子集有2^1,2个1的数的子集有2^2,k个1的数2^k,...然后加起来就是(2+1)^n == 3^n;
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cstdlib> 6 #include<vector> 7 #include<cmath> 8 using namespace std; 9 const int N = 1<<16; 10 char s[20]; 11 int n; 12 vector <int > q; 13 int check(int x){ 14 char ts[20]; 15 int c = 0; 16 for (int i = 0; i < n; i++ ){ 17 if (x&(1<<i)) ts[c++] = s[i]; 18 ts[c] = 0; 19 } 20 21 int k = 0,l , r; 22 if (c%2){ 23 l = r = c/2; 24 }else { 25 l = c/2-1; r = c/2; 26 } 27 while (ts[l-k] == ts[r+k] && l-k>=0 && r+k<c){ 28 k++; 29 } 30 if (r+k == c) { 31 return 1; 32 } 33 return 0; 34 } 35 int dp[N],can[N]; 36 void init(){ 37 memset(can,0,sizeof(can)); 38 for (int i = 1; i < (1<<n); i++){ 39 if (check(i)){ 40 can[i] = 1; 41 } 42 } 43 44 } 45 void solve(){ 46 for (int i = 0; i < (1<<n); i++) dp[i] = n; 47 dp[0] = 0; 48 for (int i = 1; i < (1<<n); i++){ 49 for (int j = i; j ; j = i&(j-1)){ 50 if (can[j]) 51 dp[i] = min(dp[i] , dp[i^j]+1); 52 } 53 } 54 printf("%d\n",dp[(1<<n)-1]); 55 } 56 int main(){ 57 int T; scanf("%d",&T); 58 while (T--){ 59 scanf("%s",s); 60 n = strlen(s); 61 init(); 62 solve(); 63 } 64 return 0; 65 }
hdu 4630
题意:给你一个1~n的排序a1~an,Q次询问在区间[l,r]中的最大gcd(a,b).a,b属于[l,r];
分析:离线,从左到右扫一遍,idx[x]记录的是扫到当前位置时,含有x因子的最右的数的位置;
这样对于当前位置的ai,找出ai的所有约数,对于约数bi,在线段树中更新 在前面出现的含有bi的最右边的数的那个位置的值,也就是idx[bi]处更新出现的最大约数;
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<vector> 5 #include<algorithm> 6 #include<cmath> 7 #include<cstdlib> 8 #define lson l,m,rt<<1 9 #define rson m+1,r,rt<<1|1 10 using namespace std; 11 const int N = 50000+10; 12 int mx[N<<2]; 13 int idx[N]; 14 int a[N]; 15 int n,Q; 16 struct Line{ 17 int x,y; 18 int id,ans; 19 Line(){} 20 Line (int x,int y):x(x),y(y){} 21 bool operator < (const Line &p)const{ 22 return y<p.y || (y == p.y && x < p.x); 23 } 24 }L[N]; 25 bool cmp(Line a,Line b){ 26 return a.id<b.id; 27 } 28 void pushup(int rt){ 29 mx[rt] = max(mx[rt<<1] , mx[rt<<1|1]); 30 } 31 32 void update(int L,int c,int l,int r,int rt){ 33 if (l == r){ 34 if (c > mx[rt]) mx[rt] = c; 35 return; 36 } 37 int m = (l+r)>>1; 38 if (L <= m) update(L,c,lson); 39 if (m < L) update(L,c,rson); 40 pushup(rt); 41 } 42 int query(int L,int R,int l,int r,int rt){ 43 if (L<=l && r<=R){ 44 return mx[rt]; 45 } 46 int m = (l+r)>>1; 47 int t1 = 0, t2 = 0; 48 if (L <= m) t1 = query(L,R,lson); 49 if (m < R) t2 = query(L,R,rson); 50 return max(t1,t2); 51 } 52 void insert(int now){ 53 int m = (int)sqrt((double)a[now]); 54 55 for (int j = 1; j <= m ; j++){ 56 if (a[now]%j == 0){ 57 update(idx[j], j, 0, n, 1); 58 idx[j] = now; 59 if (a[now]/j !=j){ 60 int c = a[now] / j; 61 update(idx[c], c, 0, n, 1); 62 idx[c] = now; 63 } 64 } 65 } 66 } 67 void solve(){ 68 memset(mx,0,sizeof(mx)); 69 memset(idx,0,sizeof(idx)); 70 int now = 1,flag = 1; 71 for (int i = 0; i < Q; ){ 72 73 if ( flag ){ 74 flag = 0; insert(now); 75 } 76 if (L[i].y == now){ 77 L[i].ans = query(L[i].x, L[i].y, 0, n, 1); 78 i++; 79 } 80 if (now < L[i].y ){ 81 flag = 1; now++; 82 } 83 } 84 sort(L,L+Q,cmp); 85 86 for (int i = 0; i < Q; i++){ 87 printf("%d\n",L[i].ans); 88 } 89 } 90 int main(){ 91 int T; scanf("%d",&T); 92 while (T--){ 93 scanf("%d",&n); 94 for (int i = 1; i <= n; i++){ 95 scanf("%d",&a[i]); 96 } 97 scanf("%d",&Q); 98 for (int i = 0; i < Q; i++){ 99 scanf("%d%d",&L[i].x,&L[i].y); 100 L[i].id = i; 101 } 102 sort(L,L+Q); 103 solve(); 104 } 105 return 0; 106 }
hdu 4631
题意:依次给你n个点,每次求出当前点中的最近点对,输出所有最近点对的和;
分析:按照x排序,然后用set维护,每次插入只更新当前点和插入点前后几个位置,如果最近距离变为0就break;
因为点的坐标的构造方法,所以是可以过的,(其实我也不太懂);
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<iostream> 5 #include<vector> 6 #include<cstdio> 7 #include<cstdlib> 8 #include<set> 9 using namespace std; 10 typedef long long LL; 11 const int N = 110000; 12 int n; 13 LL ax,bx,cx,ay,by,cy; 14 struct Point{ 15 LL x,y; 16 Point(){} 17 Point(LL x,LL y):x(x),y(y){}; 18 bool operator < (const Point &p)const{ 19 return x < p.x || (x == p.x && y < p.y); 20 } 21 void ot(){ 22 cout<<x<<" "<<y<<endl; 23 } 24 }; 25 LL sqr(LL x){ 26 return x*x; 27 } 28 LL Distance(Point a,Point b){ 29 return sqr((LL)a.x - b.x) + sqr((LL)a.y - b.y); 30 } 31 Point p[100]; 32 multiset<Point> st; 33 multiset<Point> :: iterator it1,it2,it3; 34 35 void solve(){ 36 st.clear(); 37 LL ans = 0; 38 LL nx = 0, ny = 0; 39 nx = ( bx ) % cx; 40 ny = ( by ) % cy; 41 st.insert(Point(nx,ny)); 42 LL mi = -1; 43 for (int i = 1; i < n; i++){ 44 nx = ((LL)nx * ax + bx ) % cx; 45 ny = ((LL)ny * ay + by ) % cy; 46 Point t = Point(nx,ny); 47 st.insert(t); 48 it1 = it2 = it3 = st.lower_bound(t); 49 int k = 10; 50 while (k--){ 51 if (it1 != st.begin()) it1--; 52 if (it3 != it1){ 53 LL d1 = Distance(t,*it1); 54 if (mi == -1 || d1<mi){ 55 mi = d1; 56 } 57 } 58 if (it2 != st.end()) it2++; 59 if (it2 != st.end() && it2!= it3){ 60 LL d2 = Distance(t,*it2); 61 if (mi == -1 || d2 < mi){ 62 mi = d2; 63 } 64 } 65 } 66 ans += mi; 67 if (mi == 0) break; 68 } 69 printf("%I64d\n",ans); 70 } 71 int main(){ 72 int T; scanf("%d",&T); 73 while (T--){ 74 scanf("%d",&n); 75 scanf("%d%d%d%d%d%d",&ax,&bx,&cx,&ay,&by,&cy); 76 solve(); 77 } 78 return 0; 79 }