A. Playing with Paper
题目大意
给一个大小为
a∗b
的长方形纸片反复做如下操作:
直到纸片变成了一个正方形,问这样做最终得到了多少个正方形(最后剩下的那个正方形也算)。
题解
比较好想到一个递归的做法,就是每次模拟把
a∗b
大小的棋盘变成
b∗(a−b)
大小的棋盘,但是这样做会爆栈,原因是中间递归的次数太多,但是可以发现如果这个纸片是一个很长的长方形的话,多次操作得到的正方形都是边长为
b
的,这样的操作次数是
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
typedef long long int LL;
LL calc(LL a,LL b)
{
if(!a||!b) return 0;
if(a==b) return 1;
if(a<b) swap(a,b);
return calc(b,a%b)+a/b;
}
int main()
{
LL a,b;
scanf("%I64d%I64d",&a,&b);
printf("%I64d\n",calc(a,b));
return 0;
}
B - Error Correct System
题目大意
给出两个字符串 S、T ,定义它们的距离是它们同一位上字母不相同的位数和,要你选择 S 上的两个位置的字母并交换它们,求交换后两个字符串的最小距离是多少。
题解
结果无非是三种情况:
1、距离减少2,即交换两个位置的字母后,正好让被交换的两个位置对应起来了,如
2、距离减少1,如
3、距离无法减少
于是可以用一个数组
注意细节情况,此题数据多,细节多,很容易WA
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <map>
#define MAXN 210000
using namespace std;
int pos[400][400],has[400]; //has[i]=字母i在T串中的位置
char S[MAXN],T[MAXN];
int dist=0;
int main()
{
int n;
scanf("%d",&n);
scanf("%s",S+1);
scanf("%s",T+1);
for(int i=1;i<=n;i++)
dist+=S[i]!=T[i];
if(dist<=1) //!!!!
{
printf("%d\n-1 -1\n",dist);
return 0;
}
for(int i=1;i<=n;i++)
if(S[i]!=T[i])
pos[(int)S[i]][(int)T[i]]=i,has[(int)S[i]]=i; //fuck!!!!
for(char a='a';a<='z';a++) //注意先考虑距离减2再考虑距离-1的情况
for(char b='a';b<='z';b++)
{
if(pos[(int)a][(int)b]&&pos[(int)b][(int)a])
{
printf("%d\n%d %d\n",dist-2,pos[(int)a][(int)b],pos[(int)b][(int)a]);
return 0;
}
}
for(char a='a';a<='z';a++)
for(char b='a';b<='z';b++)
{
if(pos[(int)a][(int)b]&&has[(int)b])
{
printf("%d\n%d %d\n",dist-1,pos[(int)a][(int)b],has[(int)b]);
return 0;
}
}
printf("%d\n-1 -1\n",dist);
return 0;
}
C. Glass Carving
题目大意
给一个初始大小为
W∗H
的玻璃,要对玻璃切
n
刀,每一刀的刀痕要么平行于
题解
STL题。
用两个set维护
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <set>
using namespace std;
typedef long long int LL;
set<int>sx,sy; //保存x方向和y方向的分割线
set<int>::iterator l,r; //当前分割线分割开的那个部件的两个边界线
multiset<int>lenx,leny; //两个方向上相邻分割线所夹区间的长度
int w,h,n;
int main()
{
scanf("%d%d%d",&w,&h,&n);
sx.insert(0),sx.insert(w);
sy.insert(0),sy.insert(h);
lenx.insert(w),leny.insert(h);
for(int i=1;i<=n;i++)
{
char cmd[10];
int x;
scanf("%s",cmd);
scanf("%d",&x);
if(cmd[0]=='H')
{
r=sy.lower_bound(x);
l=r;
l--;
sy.insert(x);
leny.insert((*r)-x); //加入分割后得到的两个新区间
leny.insert(x-(*l));
leny.erase(leny.find((*r)-(*l))); //把被分割的那个区间删除
}
else
{
r=sx.lower_bound(x);
l=r;
l--;
sx.insert(x);
lenx.insert((*r)-x); //加入分割后得到的两个新区间
lenx.insert(x-(*l));
lenx.erase(lenx.find((*r)-(*l))); //把被分割的那个区间删除
}
printf("%I64d\n",(LL)(*lenx.rbegin())*(LL)(*leny.rbegin()));
}
return 0;
}
D.Clique Problem
题目大意
给出
思路
题目说得非常坑爹,而且还扯什么NP问题汗= =b。其实题目非常简单,可以转化为坐标轴上
n
个区间,每个区间是
实际上非常搞笑,这是个很简单的贪心问题,只需要对所有区间按照 xi+wi 升序排序即可(就是对区间右端点升序排序),然后从左到右扫一遍所有区间,用一个指针维护当前扫过的最右边的端点坐标 maxr ,若新的区间的左端点 >=maxr ,则将这个区间加入集合,最终便能得到答案。
虽然此题很傻逼,但是我还是打错了一个大于号(打成了小于号),WA了一发,我是傻叉。。。
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 210000
using namespace std;
struct Circle
{
int x,r;
}circles[MAXN];
bool cmp(Circle a,Circle b)
{
return a.x+a.r<b.x+b.r;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&circles[i].x,&circles[i].r);
sort(circles+1,circles+n+1,cmp);
int maxr=circles[1].x+circles[1].r,ans=1;
for(int i=2;i<=n;i++)
{
if(circles[i].x-circles[i].r>=maxr)
{
ans++;
maxr=circles[i].x+circles[i].r; //!!!!!!!
}
}
printf("%d\n",ans);
return 0;
}
E.Data Center Drama
题目大意
给出
n
点
题解
非常不错的一道图论的构造题。
注意到最终的图是一个欧拉回路,那么每个点最终的入度等于出度,如果这个欧拉回路长是偶数的话,就相当于把这个图变成无向图(所有边加上反向边),并且新图的每个点的度数均为偶数。因此我们就需要在原图中,对于所有度数为奇数的点,依次连无向边,这样原图就变成了所有点度数均为偶数了,然后再对这个无向图,随便找个起点做欧拉回路,得到这个欧拉回路的路径,要把这个无向图的欧拉回路变成一个有向图的欧拉回路就很容易了,假如原来无向图的欧拉回路是a-b-c-d-e这样的形式,只需要把它变成a->b<-c->d<-e……这样的形式,就是给原来的无向图的每条边确定了方向,这样做把每个点在无向图中的偶数的度数均匀地分成了一半出度、一半入度,构造出的新有向图就是一个欧拉回路 了,但是前提就是这个欧拉回路的长度为偶数,这是很显然的。
那么如果把奇数度数点依次加完边后无向边总数为奇数怎么办?随便找个点加自环即可。
但是此题还有很重要的一点需要注意:做欧拉回路时,如果不做优化,把之前访问过的废边都访问的话,整个程序的复杂度就是 ∑degree[i]2 ,会TLE,因此要在做欧拉回路时,每访问完一条边,就把这条边删掉,最好实现的方式就是用multiset当作邻接表存边了(因为有自环所以不能用set),另外自环的边也是要加两遍的。
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <set>
#define MAXV 600000
#define MAXE 1600000
using namespace std;
vector<int>odd;
vector<int>ans;
multiset<int>G[MAXV]; //因为有自环的边所以要用多重集!!!
int nCount=0,tot_edge=0; //tot_edge=加入的不同的边的总个数
int degree[MAXV]; //度
inline void AddEdge(const int &U,const int &V,const int &I)
{
G[U].insert(V);
}
void DFS(int u)
{
while(!G[u].empty())
{
int v=*G[u].begin();
//cout<<u<<' '<<v<<endl;
G[u].erase(G[u].begin());
G[v].erase(G[v].find(u));
DFS(v);
}
ans.push_back(u);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
degree[u]++,degree[v]++;
AddEdge(u,v,++tot_edge);
AddEdge(v,u,tot_edge);
}
for(int i=1;i<=n;i++)
if(degree[i]&1)
odd.push_back(i);
for(int i=0;i<(int)odd.size();i+=2)
{
int u=odd[i],v=odd[i+1];
AddEdge(u,v,++tot_edge);
AddEdge(v,u,tot_edge);
}
if(tot_edge&1) //只有奇数条边,那么需要加一条自环的边
AddEdge(1,1,++tot_edge),AddEdge(1,1,tot_edge); //加两遍!!!!
DFS(1);
printf("%d\n",tot_edge);
for(int i=0;i<(int)ans.size()-1;i++)
{
if(i&1)
printf("%d %d\n",ans[i],ans[i+1]);
else
printf("%d %d\n",ans[i+1],ans[i]);
}
return 0;
}