@sensitive-cs
2017-04-15T22:55:05.000000Z
字数 6228
阅读 1054
题解
A - Binary Simulation
题意:
给出一个只包含0和1的字符串,有两种操作,第一是改变下标从i到j的数,让0变成1,1变成0,;第二种操作是查询下标为i的是0还是1.
思路:
线段树,每次翻转,就让所在区间改变的次数加1,不对数进行改变,最后根据改变次数的奇偶性判断是否改变。
代码:
#include <string.h>
#include <stdio.h>
int a[100005];
int add[400005];
int sum[400005];
char b[100005];
void pushup(int rt)
{
sum[rt] = sum[rt << 1] + sum[rt << 1|1];
}
void build(int l,int r,int rt)
{
if (l == r)
{
sum[rt] = a[l];
return;
}
int m = (l+r) >> 1;
build(l,m,rt << 1);
build(m+1,r,rt << 1|1);
pushup(rt);
}
void pushdown(int rt,int ln,int rn)
{
if (add[rt])
{
add[rt << 1] += add[rt];
add[rt << 1|1] += add[rt];
sum[rt << 1] += ln * add[rt];
sum[rt << 1|1] += rn *add[rt];
add[rt] = 0;
}
}
void update(int ll,int rr,int c,int l,int r,int rt)
{
if (ll <= l && r <= rr)
{
sum[rt] += c*(r-l+1);
add[rt] += c;
return;
}
int m = (l+r) >> 1;
pushdown(rt,m-l+1,r-m);
if (ll <= m) update(ll,rr,c,l,m,rt << 1);
if (rr > m) update(ll,rr,c,m+1,r,rt << 1|1);
pushup(rt);
}
long long query(int ll,int rr,int l,int r,int rt)
{
if (ll <= l && r <= rr)
{
return sum[rt];
}
int m = (l+r) >> 1;
pushdown(rt,m-l+1,r-m);
long long ans = 0;
if (ll <= m) ans += query(ll,rr,l,m,rt << 1);
if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);
return ans;
}
int main()
{
int t;
scanf ("%d",&t);
int cnt = 0;
while (t--)
{
memset(a,0,sizeof(a));
memset(add,0,sizeof(add));
memset(sum,0,sizeof(sum));
printf("Case %d:\n",++cnt);
int q = 0,sl = 0;
scanf("%s",b);
sl = strlen(b);
build(1,sl,1);
scanf("%d",&q);
for (int i = 0;i < q;i++)
{
char s[5];
int x,y;
scanf("%s",s);
if (s[0] == 'I')
{
scanf("%d%d",&x,&y);
update(x,y,1,1,sl,1);
}
if (s[0] == 'Q')
{
scanf("%d",&x);
int ans = query(x,x,1,sl,1);
if (ans % 2 == 0)
{
printf("%c\n",b[x-1]);
}
else
{
if (b[x-1] == '0') printf("1\n");
else printf("0\n");
}
}
}
}
return 0;
}
B - Points in Segments
题意:
给出n个数,后面给出m个区间,对于每个区间,查询再次区间内的数有多少个。
思路:
线段树+离散化。需要注意的是离散化时需要对不连续的两个数之间加入一个在两数之间的数,这样才能保证不出错。第二是线段树初始化的时候区间长度加上2*10e5,这样保证了所有数据都在区间内;第三呢,对输入的区间进行特判,如果左端点大于最大数或者右端点小于最小数,那就输出0.
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;
const int len = 1e5 + 5;
vector<int> v;
int a[len];
bool pos[len * 10];
int ans = 0;
struct ss
{
int l,r;
int sum;
}node[len * 10];
void build(int rt,int l,int r)
{
node[rt].l = l,node[rt].r = r;
if (l == r)
{
if (pos[l]) node[rt].sum = 1;
else node[rt].sum = 0;
return;
}
int mid = (l + r) >> 1;
build(rt << 1,l,mid);
build(rt << 1|1,mid+1,r);
node[rt].sum += (node[rt << 1].sum+node[rt << 1|1].sum);
}
void query(int rt,int L,int R)
{
if (node[rt].l >= L && node[rt].r <= R)
{
ans += node[rt].sum;
return;
}
int mid = (node[rt].l + node[rt].r) >> 1;
if (L <= mid) query(rt << 1,L,R);
if (R > mid) query(rt << 1|1,L,R);
}
int main()
{
int t,cnt = 0;
scanf("%d",&t);
while (t--)
{
memset(a,0,sizeof(a));
v.clear();
memset(pos,0,sizeof(pos));
memset(node,0,sizeof(node));
int n,q;
scanf("%d%d",&n,&q);
for (int i = 0;i < n;i++)
{
scanf("%d",&a[i]);
v.push_back(a[i]);
}
printf("Case %d:\n",++cnt);
sort(a,a+n);
sort(v.begin(),v.end());
int tt = v.size();
for (int i = 1;i < tt;i++)
{
if (v[i] - v[i-1] > 1) v.push_back(v[i] - 1);
}
int ls = v.size();
sort(v.begin(),v.end());
for (int i = 0;i < n;i++)
{
vector<int>::iterator it;
it = lower_bound(v.begin(),v.end(),a[i]);
pos[it - v.begin()+1] = 1;
}
build(1,1,ls + 2*len);
for (int i = 0;i < q;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if (x > a[n-1] || y < a[0])
{
printf("%d\n",0);
continue;
}
ans = 0;
vector<int>::iterator it;
x = lower_bound(v.begin(),v.end(),x) - v.begin() + 1;
y = lower_bound(v.begin(),v.end(),y) - v.begin() + 1;
query(1,x,y);
printf("%d\n",ans);
}
}
return 0;
}
C - Calm Down
题意:
给出大圆的半径和小圆的个数,求出小圆的最大半径,保证所有的小圆半径相同且所有的小圆相切。
思路:
所有相邻小圆的圆心相连构成一个正多边形,根据正多边形的内角和公式和三角函数以及长度关系可以推出大圆与小圆的半径的关系。数学题。
代码:
#include <stdio.h>
#include <string.h>
#include <math.h>
int main()
{
int t;
scanf("%d",&t);
int cnt = 0;
while (t--)
{
int n;
double r;
scanf("%lf%d",&r,&n);
double ans = 0;
const double pi = atan(1)*4;
if (n == 2)
{
ans = r / 2;
}
else
{
double s = (n-2) * 180.0 / n;
s = s * pi / 180 / 2;
ans = r * cos(s) / (1 + cos(s));
}
printf("Case %d: %.8f\n",++cnt,ans);
}
return 0;
}
D - Neighbor House
题意:
给出n个房子,每个房子可涂3种颜色,涂每种颜色的价格不同,相邻两间房子的颜色不能相同,问最小的花费。
思路:
动态规划,转移方程 : dp[i][1] = min(dp[i-1][2]+a[i][1],dp[i-1][3]+a[i][1]),其他与此类似。
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int a[25][5];
int dp[25][5];
const int inf = 0x3f3f3f3f;
int main()
{
int t;
int cnt = 0;
scanf("%d",&t);
while (t--)
{
memset(a,0,sizeof(a));
int n;
scanf("%d",&n);
for (int i = 1;i <= n;i++)
for (int j = 1;j <= 3;j++)
scanf("%d",&a[i][j]);
memset(dp,inf,sizeof(dp));
dp[1][1] = a[1][1];
dp[1][2] = a[1][2];
dp[1][3] = a[1][3];
for (int i = 2;i <= n;i++)
{
dp[i][1] = min(dp[i-1][2] + a[i][1],dp[i-1][3] + a[i][1]);
dp[i][2] = min(dp[i-1][1] + a[i][2],dp[i-1][3] + a[i][2]);
dp[i][3] = min(dp[i-1][2] + a[i][3],dp[i-1][1] + a[i][3]);
}
int minn = inf;
for (int i = 1;i <= 3;i++)
if (dp[n][i] < minn) minn = dp[n][i];
printf("Case %d: %d\n",++cnt,minn);
}
return 0;
}
E - Array Queries
题意:
给出n个数,每次查询下标从i到j的最小值。
思路:
线段树,维护的是区间的最小值。
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct tt
{
int l,r;
int v;
}node[100005 << 2];
void build(int l,int r,int rt)
{
node[rt].l = l,node[rt].r = r;
if (l == r)
{
scanf("%d",&node[rt].v);
return;
}
int mid = (l + r) >> 1;
build(l,mid,rt << 1);
build(mid + 1,r,rt << 1|1);
node[rt].v = min(node[rt << 1].v,node[rt << 1|1].v);
}
int query(int rt,int ll,int rr)
{
if (node[rt].l >= ll && node[rt].r <= rr)
{
return node[rt].v;
}
int a1 = 1e7,a2 = 1e7;
int mid = (node[rt].l + node[rt].r) / 2;
if (ll <= mid) a1 = query(rt << 1,ll,rr);
if (rr > mid) a2 = query(rt << 1|1,ll,rr);
return min(a1,a2);
}
int main()
{
int t;
scanf("%d",&t);
int cnt = 0;
while (t--)
{
memset(node,0,sizeof(node));
int n,q;
scanf("%d%d",&n,&q);
build(1,n,1);
printf("Case %d:\n",++cnt);
for (int i = 0;i < q;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int ans = query(1,x,y);
printf("%d\n",ans);
}
}
return 0;
}
F - Farthest Nodes in a Tree
题意:
给出一棵树,找出任意两点间距离的最大值。
思路:
求一棵树的直径。结论:从任意一个点总能找到距离它最远的点,此点即是最长路的终点。
所以两次bfs,第一次bfs找到最长路终点,第二次算出终点到所有点的距离,最后找出最大值即可。
代码:
#include <stdio.h>
#include <string.h>
#include <vector>
#include <map>
#include <queue>
using namespace std;
int d[30005];
typedef pair<int,int> p;
map<p,int> mmp;
vector<int> g[30005];
bool vis[30005];
int main()
{
int t;
scanf("%d",&t);
int cnt = 0;
while (t--)
{
int n;
mmp.clear();
memset(g,0,sizeof(g));
memset(vis,0,sizeof(vis));
scanf("%d",&n);
for (int i = 0;i < n-1;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
mmp[p(x,y)] = mmp[p(y,x)] = z;
g[x].push_back(y);
g[y].push_back(x);
}
memset(d,0,sizeof(d));
queue<int> s;
s.push(0);
vis[0] = 1;
while (!s.empty())
{
int c = s.front();s.pop();
for (int i = 0;i < g[c].size();i++)
{
if (!vis[g[c][i]])
{
int k = g[c][i];
d[k] = d[c] + mmp[p(c,k)];
vis[k] = 1;
s.push(k);
}
}
}
int m;
int maxnn = 0;
for (int i = 0;i < n;i++)
{
if (maxnn < d[i])
{
m = i;
maxnn = d[i];
}
}
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
s.push(m);
vis[m] = 1;
while (!s.empty())
{
int c = s.front();s.pop();
for (int i = 0;i < g[c].size();i++)
{
if (!vis[g[c][i]])
{
int k = g[c][i];
d[k] = d[c] + mmp[p(c,k)];
vis[k] = 1;
s.push(k);
}
}
}
maxnn = 0;
for (int i = 0;i < n;i++)
{
if (maxnn < d[i])
{
m = i;
maxnn = d[i];
}
}
printf("Case %d: %d\n",++cnt,maxnn);
}
return 0;
}