标签:
上星期生病结果没写题QAQ 健康还是很重要的!
20160516
1、bzoj3932 http://www.lydsy.com/JudgeOnline/problem.php?id=3932
题意:m个任务,任务(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束,优先级为Pi。n个询问,每次询问第Xi秒正在运行的任务中,优先级最小的Ki个任务的优先级之和是多少。若Ki大于第Xi秒正在运行的任务总数,输出第Xi秒任务优先级之和。m,n≤100000,强制在线。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 400000 7 #define ll long long 8 using namespace std; 9 10 struct opt{int a,b,c,id;}; opt opts[maxn*2]; 11 bool cmp1(opt a,opt b){return a.b<b.b;} 12 bool cmp2(opt a,opt b){return a.a<b.a;} 13 int lc[20*maxn],rc[20*maxn],rt[maxn],sz[20*maxn],n,m,valn,tot,optn; 14 ll sm[20*maxn]; 15 void build(int &x,int l,int r){ 16 x=++tot; lc[x]=rc[x]=sz[x]=sm[x]=0; if(l==r)return; 17 int mid=(l+r)>>1; build(lc[x],l,mid); build(rc[x],mid+1,r); 18 } 19 void ins(int &x,int l,int r,int y,int z1,int z2){ 20 tot++; sm[tot]=sm[x]+(ll)(z1*z2); sz[tot]=sz[x]+z2; 21 lc[tot]=lc[x]; rc[tot]=rc[x]; x=tot; if(l==r)return; int mid=(l+r)>>1; 22 if(y<=mid)ins(lc[x],l,mid,y,z1,z2);else ins(rc[x],mid+1,r,y,z1,z2); 23 } 24 ll query(int x,ll k){ 25 ll q=0; int y=x; 26 while(1){ 27 if(k>=sz[y]){q+=sm[y]; return q;} if(!lc[y]&&!rc[y]){q+=sm[y]/sz[y]*k; return q;} 28 if(k==sz[lc[y]]){q+=sm[lc[y]]; return q;} 29 if(k<sz[lc[y]])y=lc[y];else k-=sz[lc[y]],q+=sm[lc[y]],y=rc[y]; 30 } 31 } 32 int main(){ 33 scanf("%d%d",&m,&n); optn=0; 34 inc(i,1,m){ 35 int a,b,c; scanf("%d%d%d",&a,&b,&c); 36 opts[++optn]=(opt){a,c,1,0}; if(b!=n)opts[++optn]=(opt){b+1,c,-1,0}; 37 } 38 sort(opts+1,opts+1+optn,cmp1); valn=1; opts[1].id=1; 39 inc(i,2,optn){if(opts[i].b!=opts[i-1].b)opts[i].id=++valn;else opts[i].id=valn;} 40 tot=0; build(rt[0],1,valn); sort(opts+1,opts+1+optn,cmp2); opts[optn+1].a=n; 41 for(int i=1;i<=opts[1].a&&i<=n;i++)rt[i]=rt[i-1]; 42 inc(i,1,optn){ 43 ins(rt[opts[i].a],1,valn,opts[i].id,opts[i].b,opts[i].c); 44 for(int j=opts[i].a+1;j<=opts[i+1].a&&j<=n;j++)rt[j]=rt[j-1]; 45 } 46 ll last=1; 47 inc(i,1,n){ 48 int a;ll b,c,d; scanf("%d%lld%lld%lld",&a,&b,&c,&d); 49 last=query(rt[a],1+(b*last+c)%d); 50 printf("%lld\n",last); 51 } 52 return 0; 53 }
题解:第一次写主席树……(因为没彻底理解被yyl大爷d:你根本不理解主席树)主席树本质上就是权值线段树+重用节点。
反映在本题中,就是给每个时间点建一棵权值线段树,但这样会MLE,所以我们先将所有任务拆成“Si到n时间点的权值+Pi”和“Ei+1到n时间点的权值+(-Pi),然后按插入的时间点排序,在每个插入操作前,将之前上一次操作得到的权值线段树的根节点指针复制过来,然后插入时只新开节点记录被修改后的节点,因为一次插入只会影响log2n个节点,所以总空间复杂度为O(nlog2n),同时因为相隔两个插入时间点之间的时间点只要复制一个指针就行,每次插入时间复杂度为log2n,故时间复杂度为O(nlog2n)。本题可以不离散化,但我比较怂所以还是离散了一下省空间。
20160517
2、bzoj4512 http://www.lydsy.com/JudgeOnline/problem.php?id=4512
题意:某人从农场的(0,0)出发,沿边界到处乱走,走过的地方会留下栅栏,等走完后问要在多少个栅栏上开门才能使整个农场连通,最多走1000步。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 using namespace std; 6 7 bool unok[2000][2000][4],vis[2000][2000]; int mx,my,tot,n; 8 char opt[2000]; 9 void dfs(int x,int y){ 10 vis[x][y]=1; 11 if(y!=0&&!unok[x][y][0]&&!vis[x][y-1])dfs(x,y-1); 12 if(x!=mx&&!unok[x][y][1]&&!vis[x+1][y])dfs(x+1,y); 13 if(y!=my&&!unok[x][y][2]&&!vis[x][y+1])dfs(x,y+1); 14 if(x!=0&&!unok[x][y][3]&&!vis[x-1][y])dfs(x-1,y); 15 } 16 int main(){ 17 scanf("%d",&n); int x=1,y=1; scanf("%s",opt); 18 inc(i,0,n-1){if(opt[i]==‘W‘)y++;if(opt[i]==‘S‘)x++;} 19 memset(unok,0,sizeof(unok)); mx=my=0; 20 inc(i,0,n-1){ 21 if(opt[i]==‘N‘){ 22 unok[x][y][0]=1; unok[x][y-1][2]=1; x++; mx=max(mx,x); 23 } 24 if(opt[i]==‘S‘){ 25 unok[x-1][y][0]=1; unok[x-1][y-1][2]=1; x--; 26 } 27 if(opt[i]==‘W‘){ 28 unok[x][y-1][3]=1; unok[x-1][y-1][1]=1; y--; 29 } 30 if(opt[i]==‘E‘){ 31 unok[x][y][3]=1; unok[x-1][y][1]=1; y++; my=max(my,y); 32 } 33 } 34 memset(vis,0,sizeof(vis)); tot=0; 35 inc(i,0,mx)inc(j,0,my)if(!vis[i][j])tot++,dfs(i,j); 36 printf("%d",tot-1); 37 return 0; 38 }
题解:我的代码比别人的都长~我的做法是先算出最左/最下可能会走到哪里,然后变换一下坐标系(实际是是改变出发起点),然后记录哪个格子的上下左右被栅栏堵了,最后做一下floodfill,输出连通块数-1。注意还要把有栅栏区域的外圈格子也算进去,因为它们代表了有栅栏区域外的广大地区(这个人的农场是无限大的)。
3、bzoj4525 http://www.lydsy.com/JudgeOnline/problem.php?id=4525
题意:有N个草堆在数轴的不同位置,向坐标x处扔炸弹,[x−R,x+R]的草堆都会燃爆。 K个炸弹,问如果要引爆所有的草堆最小的R。草堆数最多50000,坐标最大为109
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define INF 0x3fffffff 6 using namespace std; 7 8 int n,k,x[60000]; 9 int main(){ 10 scanf("%d%d",&n,&k); inc(i,1,n)scanf("%d",&x[i]); sort(x+1,x+n+1); 11 int l=0,r=x[n],ans; 12 while(l<=r){ 13 int mid=(l+r)>>1,pre=-INF,tot=0; bool f=0; 14 inc(i,1,n){ 15 if(x[i]-pre>2*mid)tot++,pre=x[i]; 16 if(tot>k){f=1; break;} 17 } 18 if(!f)r=mid-1,ans=mid;else l=mid+1; 19 } 20 printf("%d",ans); 21 return 0; 22 }
题解:二分R,判定时从小到大枚举草堆,如果这个草堆没被炸就在这里放炸弹(实际上是放在此处坐标+R的位置),炸掉与它距离≤2R的草堆,重复上述操作。本弱一开始以为炸掉的是与它距离≤2R+1的草堆,调了0.5hQAQ
4、bzoj1051 http://www.lydsy.com/JudgeOnline/problem.php?id=1051
题意:有N头牛,给M对整数(A,B),表示牛A认为牛B受欢迎,这种关系具有传递性。求出有多少头牛被所有的牛认为是受欢迎的。N≤10000
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <stack> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 using namespace std; 7 8 struct e{int f,t,n;}; e es[60000]; int g[20000],ess; 9 void pe(int f,int t){es[++ess]=(e){f,t,g[f]}; g[f]=ess;} 10 int bel[20000],cnt[20000],n,m,sz[20000],tot,low[20000],pre[20000],tim; bool vis[20000],ins[20000]; 11 stack <int> s; 12 void dfs(int x){ 13 vis[x]=ins[x]=1; s.push(x); low[x]=pre[x]=++tim; 14 for(int i=g[x];i;i=es[i].n) 15 if(!vis[es[i].t])dfs(es[i].t),low[x]=min(low[x],low[es[i].t]); 16 else if(ins[es[i].t])low[x]=min(low[x],pre[es[i].t]); 17 if(low[x]==pre[x]){ 18 tot++; 19 while(!s.empty()){ 20 int now=s.top(); s.pop(); bel[now]=tot; sz[tot]++; ins[now]=0; if(now==x)break; 21 } 22 } 23 } 24 void tarjan(){ 25 while(!s.empty())s.pop(); memset(vis,0,sizeof(vis)); memset(ins,0,sizeof(ins)); memset(sz,0,sizeof(sz)); 26 tot=tim=0; inc(i,1,n)if(! vis[i])dfs(i); 27 } 28 void solve(){ 29 memset(cnt,0,sizeof(cnt)); inc(i,1,ess)if(bel[es[i].f]!=bel[es[i].t])cnt[bel[es[i].f]]++; int a=0; 30 inc(i,1,tot){ 31 if(cnt[i]==0&&a!=0){printf("0\n"); return;} 32 if(cnt[i]==0)a=i; 33 } 34 printf("%d\n",sz[a]); 35 } 36 int main(){ 37 scanf("%d%d",&n,&m); ess=0; memset(g,0,sizeof(g)); 38 inc(i,1,m){int a,b; scanf("%d%d",&a,&b); pe(a,b);} 39 tarjan(); solve(); 40 return 0; 41 }
题解:因为求的是被所有牛认同的牛,如果该牛不认同任何牛,那么这头牛出度为0,且出度为0的牛有且只有一个否则不存在所求牛。如果这头牛认同别的牛,那么就要求这两头牛互相认同。同时两头牛都是所求牛。因此做个tarjan缩点,缩点后若出度为0的点有多个则没有所求牛,若只有一个那么这个点所表示强连通块里的所有点都是所求牛。
5、bzoj1079 http://www.lydsy.com/JudgeOnline/problem.php?id=1079
题意:有n个木块排成一行,有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块,所有油漆刚好足够涂满所有木块。求任意两个相邻木块颜色不同的着色方案。k≤15,ci≤5
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define ll long long 6 #define mod 1000000007 7 using namespace std; 8 9 ll f[16][16][16][16][16][6],k,c[16],rem[6]; 10 ll dfs(int a,int b,int c,int d,int e,int g){ 11 if(a+b+c+d+e==0)return 1; long long &ff=f[a][b][c][d][e][g]; 12 if(ff!=-1)return ff; ff=0; 13 if(a)ff=(ff+(ll)(a-(g==1))*dfs(a-1,b,c,d,e,0))%mod; 14 if(b)ff=(ff+(ll)(b-(g==2))*dfs(a+1,b-1,c,d,e,1))%mod; 15 if(c)ff=(ff+(ll)(c-(g==3))*dfs(a,b+1,c-1,d,e,2))%mod; 16 if(d)ff=(ff+(ll)(d-(g==4))*dfs(a,b,c+1,d-1,e,3))%mod; 17 if(e)ff=(ff+(ll)(e-(g==5))*dfs(a,b,c,d+1,e-1,4))%mod; 18 return ff; 19 } 20 int main(){ 21 scanf("%d",&k); inc(i,1,k)scanf("%d",&c[i]),rem[c[i]]++; 22 memset(f,-1,sizeof(f)); printf("%lld",dfs(rem[1],rem[2],rem[3],rem[4],rem[5],0)); 23 return 0; 24 }
题解:解决本题关键是ci≤5,所以以剩余可涂方块数为1,2,3,4,5及上次涂的色这次剩余可涂方块数为状态,做dp就行了。
6、bzoj1029 http://www.lydsy.com/JudgeOnline/problem.php?id=1029
题意:抢修N个建筑。修理工人一次只能修理一个建筑,如果某个建筑在一段时间之内没有完全修理完毕,这个建筑就报废了。求一个能抢修尽可能多的建筑的抢修顺序。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 using namespace std; 7 8 struct task{int t1,t2;}; task tasks[200000]; 9 bool cmp(task a,task b){return a.t2<b.t2;} 10 priority_queue <int> q; 11 int n,tim,ans,a; 12 int main(){ 13 scanf("%d",&n); inc(i,1,n)scanf("%d%d",&tasks[i].t1,&tasks[i].t2); sort(tasks+1,tasks+n+1,cmp); tim=ans=0; 14 inc(i,1,n){ 15 if(tim+tasks[i].t1<=tasks[i].t2)tim+=tasks[i].t1,q.push(tasks[i].t1),ans++; 16 else if(!q.empty()&&tasks[i].t1<(a=q.top())&&tim-a+tasks[i].t1<=tasks[i].t2) 17 tim=tim-a+tasks[i].t1,q.pop(),q.push(tasks[i].t1); 18 } 19 printf("%d",ans); 20 return 0; 21 }
题解:贪心。首先按毁坏时间排序,如果按照当前的时间计算能修好这个建筑,就修好它;如果修不好,就找以前修过的建筑,如果修理时间最长的建筑比它修理时间长,就不修这个建筑,改修当前建筑,使当前时间缩短。这个操作用优先队列维护。
20160519
7、bzoj4582 http://www.lydsy.com/JudgeOnline/problem.php?id=4582
题意:n个钻石,每个都有一个大小,现在将其装进2个盒子里,每个盒子里的钻石最大的与最小的大小不能超过k,问最多能装多少个。n最大50000。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define dec(i,j,k) for(int i=j;i>=k;i--) 6 #define maxn 60000 7 using namespace std; 8 9 int n,k,sz[maxn],r,cnt[maxn],mx[maxn]; 10 int main(){ 11 scanf("%d%d",&n,&k); inc(i,1,n)scanf("%d",&sz[i]); sort(sz+1,sz+n+1); r=1; 12 inc(i,1,n){ 13 while(r<=n&&sz[r]-sz[i]<=k)r++; cnt[i]=r-i; 14 } 15 mx[n+1]=0; dec(i,n,1)mx[i]=max(mx[i+1],cnt[i]); 16 int ans=0; inc(i,1,n)ans=max(ans,cnt[i]+mx[i+cnt[i]]); printf("%d",ans); 17 return 0; 18 }
题解:我真傻,真的~首先对大小排序,然后找以i为左端点的可装区间,这个操作两个指针就可以搞,我却以为要二分查找。预处理完了,因为不交错的区间肯定比交错的区间优,所以从n到1递推一下从n到i最大的区间大小是多少,然后枚举每个区间,找到当前区间大小加上右端点+1到n中最大的区间大小中的最大值输出即可。我却以为要找与最大区间不交错的第二大区间,结果WA了好几发,才发现这是错误的贪心QAQ
8、bzoj1040 http://www.lydsy.com/JudgeOnline/problem.php?id=1040
题意:n个骑士,每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),且有一个战斗力。求从所有
的骑士中选出一个骑士之间没有矛盾的骑士军团最大战斗力之和。n最大10e6
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 1000010 6 #define ll long long 7 using namespace std; 8 9 bool vis[maxn]; ll f[maxn][2],w[maxn]; int n,bad; 10 struct e{int f,t,n;}; e es[maxn*2]; int ess,g[maxn]; 11 void pe(int f,int t){ 12 es[++ess]=(e){f,t,g[f]}; g[f]=ess; es[++ess]=(e){t,f,g[t]}; g[t]=ess; 13 } 14 void dfs(int x,int fa){ 15 vis[x]=1; 16 for(int i=g[x];i!=-1;i=es[i].n)if(i!=fa){ 17 if(vis[es[i].t])bad=i;else dfs(es[i].t,i^1); 18 } 19 } 20 ll dp(int x,int fa,int b){ 21 if(f[x][b]!=-1)return f[x][b]; f[x][b]=0; //printf("%d %d %d %d %d\n",x,fa,b,f[x][b]); 22 for(int i=g[x];i!=-1;i=es[i].n)if(i!=fa&&i!=bad&&i!=(bad^1)){ 23 if(!b)f[x][b]+=max(dp(es[i].t,i^1,0),dp(es[i].t,i^1,1)+w[es[i].t]); 24 else f[x][b]+=dp(es[i].t,i^1,0); 25 } 26 return f[x][b]; 27 } 28 int main(){ 29 scanf("%d",&n); memset(vis,0,sizeof(vis)); ll ans=0; memset(g,-1,sizeof(g)); ess=-1; 30 inc(i,1,n){ll a; int b; scanf("%lld%d",&a,&b); w[i]=a; pe(i,b);} 31 inc(i,1,n)if(!vis[i]){ 32 dfs(i,-1); ll mx=0; 33 memset(f,-1,sizeof(f)); mx=max(mx,dp(es[bad].f,-1,0)); 34 memset(f,-1,sizeof(f)); mx=max(mx,dp(es[bad].t,-1,0)); 35 ans+=mx; 36 } 37 printf("%lld",ans); 38 }
题解:厌恶关系实际上是无向的。从每个骑士出发,沿着关系走可以得一个基环树(就是只有一个环,且断了环就会变成一棵树的连通块)。于是先dfs找环,然后断环,分别尝试以去掉的那条边的两个节点u,v为根,作树形dp,将f[u][0]与f[v][0]的最大值计入答案。
方程:0表示当前节点不选,1表示选:f[i][0]=sum{max{f[j][0],f[j][1]+v[j]}|(i,j)∈E}
f[i][1]=sum{f[j][0]|(i,j)∈E}
注意断掉的那条边是无向边(被拆成了两条有向边),并且边的编号要作为记忆化搜索的一个参数。
20160520
9、bzoj1816 http://www.lydsy.com/JudgeOnline/problem.php?id=1816
题意:n种牌,第i种牌的数目为ci还有m张鬼。可以用每种牌各一张来组成一套牌,其中一张可以用鬼代替。求最多可组几套牌。n最大50。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 using namespace std; 6 7 int a[60],n,m; 8 bool check(int x){ 9 int y=min(x,m); inc(i,1,n)if(x>a[i]){y-=(x-a[i]); if(y<0)return 0;} return 1; 10 } 11 int main(){ 12 scanf("%d%d",&n,&m); inc(i,1,n)scanf("%d",&a[i]); 13 int l=0,r=1000000000,ans; 14 while(l<=r){ 15 int mid=l+((r-l)>>1); if(check(mid))ans=mid,l=mid+1;else r=mid-1; 16 } 17 printf("%d",ans); 18 return 0; 19 }
题解:其实这道题我不是特别理解。做法是二分可组多少套,累加套数减每个ci的差,如果这个累加和大于m与套数比较的最小值就不为可行解。还要再思考。
10、bzoj1096 http://www.lydsy.com/JudgeOnline/problem.php?id=1096
题意:N个工厂,第i个工厂目前已有成品Pi件,在第i个工厂位置建立仓库的费用是Ci。对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,产品都只能运往编号更大的工厂的仓库,一件产品运送1个单位距离的费用是1。求最小总费用(建造费用+运输费用)。N最大10e6
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 1000100 6 #define ll long long 7 using namespace std; 8 9 10 ll sump[maxn],sumxp[maxn],f[maxn],c[maxn],x[maxn]; int n,q[maxn],l,r; 11 double calc(int j,int k){ 12 return (double)(f[j]-f[k]+sumxp[j]-sumxp[k])/(double)(sump[j]-sump[k]); 13 } 14 int main(){ 15 scanf("%d",&n); sump[0]=sumxp[0]=0; 16 inc(i,1,n){ 17 ll p; scanf("%lld%lld%lld",&x[i],&p,&c[i]); sump[i]=sump[i-1]+p; sumxp[i]=sumxp[i-1]+x[i]*p; 18 } 19 l=0; r=0; q[l]=0; f[0]=0; 20 inc(i,1,n){ 21 while(l<r&&calc(q[l],q[l+1])<x[i])l++; 22 f[i]=f[q[l]]+sump[i]*x[i]-sump[q[l]]*x[i]-sumxp[i]+sumxp[q[l]]+c[i]; 23 while(l<r&&calc(q[r],i)<calc(q[r-1],q[r]))r--; q[++r]=i; 24 } 25 printf("%lld",f[n]); 26 return 0; 27 }
题解:斜率优化dp。我公式推错了3次QAQ
f[i]=max{f[j]+sigma(k,j+1,i)(x[i]-x[k])*p[k]+c[i]}
=max{f[j]+sigma(k,j+1,i)x[i]*p[k]-sigma(k,j+1,i)x[k]*p[k]+c[i]}
=max{f[j]+(sump[i]-sump[j])*x[i]-(sumxp[i]-sumxp[j])+c[i]}
=max{f[j]+sump[i]*x[i]-sump[j]*x[i]-sumxp[i]+sumxp[j]+c[i]}
f[j]+sump[i]*x[i]-sump[j]*x[i]-sumxp[i]+sumxp[j]+c[i]<f[k]+sump[i]*x[i]-sump[k]*x[i]-sumxp[i]+sumxp[k]+c[i]
f[j]-f[k]+sumxp[j]-sumxp[k]<sump[j]*x[i]-sump[k]*x[i]
(f[j]-f[k]+sumxp[j]-sumxp[k])/(sump[j]-sump[k])>x[i](j在k前面)
注意开始是单调队列里要有一个f[0],因为不需要一定在1处建仓库(本弱和标程对拍才发现这个错误,好弱啊!)
11、bzoj2338 http://www.lydsy.com/JudgeOnline/problem.php?id=2338
题意:n个顶点,找一个矩形,使其面积最大。注意:矩形的边不一定要和坐标轴平行!
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <set> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 2000 7 #define ll long long 8 using namespace std; 9 10 inline ll sqr(ll a){return a*a;} 11 struct line{ll x1,y1,x2,y2;}; line lines[maxn*maxn]; int linen; 12 inline ll length(line a){return sqr(a.x1-a.x2)+sqr(a.y1-a.y2);} 13 bool cmp(line a,line b){ 14 if(a.x1+a.x2==b.x1+b.x2&&a.y1+a.y2==b.y1+b.y2) 15 return length(a)<length(b); 16 else return a.x1+a.x2==b.x1+b.x2?a.y1+a.y2<b.y1+b.y2:a.x1+a.x2<b.x1+b.x2; 17 } 18 ll x[maxn],y[maxn]; int n; 19 int main(){ 20 scanf("%d",&n); inc(i,1,n)scanf("%lld%lld",&x[i],&y[i]); linen=0; 21 inc(i,1,n)inc(j,i+1,n)lines[++linen]=(line){x[i],y[i],x[j],y[j]}; 22 sort(lines+1,lines+1+linen,cmp); ll mx=0; 23 inc(i,1,linen-1){ 24 line &a=lines[i]; int j=i+1; 25 while(j<=linen&&length(a)==length(lines[j])&&a.x1+a.x2==lines[j].x1+lines[j].x2&&a.y1+a.y2==lines[j].y1+lines[j].y2) 26 mx=max(mx,abs((a.x2-a.x1)*(lines[j].y2-lines[j].y1)-(a.y2-a.y1)*(lines[j].x2-lines[j].x1))>>1),j++; 27 } 28 printf("%lld",mx); return 0; 29 }
题解:先将点两两组成线段,然后将它们按中点和长度排序,则每组中点和长度都相等的线段两两都可以组成矩形,比较它们的面积就行。求面积用叉积(即两个向量末端点与它们的和末端点组成的平行四边形的面积,公式:x1*y2-x2*y1或|a|*|b|*sin<a,b>)除以2。
吐槽:本蒟蒻正因为红字部分WA了,而且本题的数据似乎并没有那么大,才能这样写。
12、bzoj1854 http://www.lydsy.com/JudgeOnline/problem.php?id=1854
题意:n个装备,每种装备都有2个属性值,分别用[1,10000]之间的数表示。使用某种装备时,只能使用该装备的某一个属性。并且每种装备最多只能使用一次。攻击boss的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。求最多能连续攻击boss多少次。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 using namespace std; 6 7 bool vis[20000]; int fa[20000],n; 8 int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} 9 int main(){ 10 scanf("%d",&n); inc(i,1,10000)fa[i]=i; memset(vis,0,sizeof(vis)); 11 inc(i,1,n){ 12 int a,b,x,y; scanf("%d%d",&a,&b); x=find(a); y=find(b); 13 if(x==y)vis[x]=1;else{if(x>y)swap(x,y); fa[x]=y; vis[x]=1;} 14 } 15 inc(i,1,10001)if(!vis[i]){printf("%d",i-1); break;} 16 return 0; 17 }
题解:本题竟然用并查集!把每个装备看成1条无向边,当n条边没有组成一个环时,则一共可以得到n-1个属性,如果n条边组成了一个环,则可以得到n个属性。因此可以使用并查集,根据它除了并操作不会改变根节点的性质,用它维护一个vis数组表示第i个属性能否得到,当一条边插入时,如果两个端点不在一个联通块中,就将根节点表示属性小的那个联通块连到大的那个,并将小的联通块根节点的vis置为1,大的不变;如果在一个联通块中,就将该联通块根节点vis置为1。最后枚举一下vis从1到多少均为1就是答案。
注意:因为我的程序是枚举到vis为0的那个值退出,由于属性最大是10000,所以要枚举到10001。
标签:
原文地址:http://www.cnblogs.com/YuanZiming/p/5516484.html