码迷,mamicode.com
首页 > 其他好文 > 详细

20160515~20160521

时间:2016-05-28 17:34:35      阅读:202      评论:0      收藏:0      [点我收藏+]

标签:

上星期生病结果没写题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 }
View Code

题解:第一次写主席树……(因为没彻底理解被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 }
View Code

题解:我的代码比别人的都长~我的做法是先算出最左/最下可能会走到哪里,然后变换一下坐标系(实际是是改变出发起点),然后记录哪个格子的上下左右被栅栏堵了,最后做一下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 }
View Code

题解:二分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 }
View Code

题解:因为求的是被所有牛认同的牛,如果该牛不认同任何牛,那么这头牛出度为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 }
View Code

题解:解决本题关键是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 }
View Code

题解:贪心。首先按毁坏时间排序,如果按照当前的时间计算能修好这个建筑,就修好它;如果修不好,就找以前修过的建筑,如果修理时间最长的建筑比它修理时间长,就不修这个建筑,改修当前建筑,使当前时间缩短。这个操作用优先队列维护。

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 }
View Code

题解:我真傻,真的~首先对大小排序,然后找以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 }
View Code

题解:厌恶关系实际上是无向的。从每个骑士出发,沿着关系走可以得一个基环树(就是只有一个环,且断了环就会变成一棵树的连通块)。于是先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 }
View Code

题解:其实这道题我不是特别理解。做法是二分可组多少套,累加套数减每个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 }
View Code

题解:斜率优化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 }
View Code

 题解:先将点两两组成线段,然后将它们按中点和长度排序,则每组中点和长度都相等的线段两两都可以组成矩形,比较它们的面积就行。求面积用叉积(即两个向量末端点与它们的和末端点组成的平行四边形的面积,公式: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 }
View Code

题解:本题竟然用并查集!把每个装备看成1条无向边,当n条边没有组成一个环时,则一共可以得到n-1个属性,如果n条边组成了一个环,则可以得到n个属性。因此可以使用并查集,根据它除了并操作不会改变根节点的性质,用它维护一个vis数组表示第i个属性能否得到,当一条边插入时,如果两个端点不在一个联通块中,就将根节点表示属性小的那个联通块连到大的那个,并将小的联通块根节点的vis置为1,大的不变;如果在一个联通块中,就将该联通块根节点vis置为1。最后枚举一下vis从1到多少均为1就是答案。

注意:因为我的程序是枚举到vis为0的那个值退出,由于属性最大是10000,所以要枚举到10001。

20160515~20160521

标签:

原文地址:http://www.cnblogs.com/YuanZiming/p/5516484.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!