@Asuna
2017-04-22T22:10:02.000000Z
字数 4888
阅读 907
题解
题意:一串01序列,两种操作,一种是从i到j取反,一种是问第i个是0还是1.
题解:线段树区间更新,单点查询。把需要翻转的区间标记,记录这个区间的翻转次数,然后向下传标记,查询时只需看看这个点翻转奇数次还是偶数次。
参考代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int tre[400010],lazy[400010],t,n,len,icase;
char op[2],s[100010];
void pushdown(int rt)
{
//cout<<rt<<endl;
if (lazy[rt]!=0)
{
tre[rt*2]+=lazy[rt];
tre[rt*2+1]+=lazy[rt];
lazy[rt*2]+=lazy[rt];
lazy[rt*2+1]+=lazy[rt];
lazy[rt]=0;
}
return ;
}
void update(int L,int R,int l,int r,int rt)
{
//cout<<rt<<endl;
if (L<=l && r<=R)
{
tre[rt]++;
lazy[rt]++;
return ;
}
int mid=(l+r)/2;
pushdown(rt);
if (L<=mid) update(L,R,l,mid,rt*2);
if (mid<R) update(L,R,mid+1,r,rt*2+1);
}
int query(int L,int R,int l,int r,int rt)
{
//cout<<rt<<endl;
if (L==l && r==R)
{
// cout<<tre[rt]<<endl;
return tre[rt];
}
//cout<<L<<" "<<R<<" "<<tre[rt]<<endl;
int mid=(l+r)/2,ans=0;
pushdown(rt);
if (L<=mid) return query(L,R,l,mid,rt*2);
if (R>mid) return query(L,R,mid+1,r,rt*2+1);
return ans;
}
int main()
{
scanf("%d",&t);
while (t--)
{
scanf("%s%d",s+1,&n);
len=strlen(s+1);
printf("Case %d:\n",++icase);
memset(tre,0,sizeof(tre));
memset(lazy,0,sizeof(lazy));
while (n--)
{
scanf("%s",op);
if (op[0]=='I')
{
int l,r;
scanf("%d%d",&l,&r);
update(l,r,1,len,1);
}
else
{
int x,ans;
scanf("%d",&x);
ans=query(x,x,1,len,1);
//cout<<ans<<" "<<s[x]<<endl;
if (ans%2!=0)
{
if (s[x]=='0') printf("%d\n",1);
else printf("%d\n",0);
}
else
{
if (s[x]=='0') printf("%d\n",0);
else printf("%d\n",1);
}
}
}
}
return 0;
}
题意:题意很简单,给n个有序的数,这些数分布在一个坐标轴上。给q次查找,询问在区间x,y中有多少个数据。
题解:寻找这些点中对于每条线段的上下界即可。
参考代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int t,p,q,a[100010],icase;
int find(int x,int y)
{
int l=0,r=p-1,ret1=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(a[mid]>=x)
{
ret1=mid;
r=mid-1;
}
else l=mid+1;
}
l=0;
r=p-1;
int ret2=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(a[mid]<=y)
{
ret2=mid;
l=mid+1;
}
else r=mid-1;
}
if (ret1==-1 || ret2==-1) return 0;
return ret2-ret1+1;
}
int main()
{
scanf("%d",&t);
while(t--)
{
printf("Case %d:\n",++icase);
scanf("%d%d",&p,&q);
for (int i=0; i<p; i++) scanf("%d",&a[i]);
while (q--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",find(x,y));
}
}
return 0;
}
题意:给出大圆半径,以及其内的小圆个数,求小圆的半径。
题解:大圆中心A与小圆中心B相连,之后与2小圆交点相连,得到一个垂直三角形。
参考代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const double PI=acos(-1.0);
int main()
{
int cas,i,j=0,n;
double R,r;
scanf("%d",&cas);
while(cas--)
{
j++;
scanf("%lf %d",&R,&n);
printf("Case %d: ",j);
r=(R*sin(PI/(n*1.0)))/(1+sin(PI/(n*1.0)));
printf("%.10lf\n",r);
}
return 0;
}
题意:有n个房子,每个房子都可以染红绿蓝三种颜色,并且给出了每个房子染每种颜色费用,相邻房子不能同色,求染完房子的最小费用。
题解:定义dp[i][j]为粉刷第i个房子用的颜色j。
转移方程:dp[i][j]=min(dp[i-1][(j+1)%3] , dp[i-1][(j+2)%3]);
一共有三种颜色{0,1,2},任取一种颜色{j},那么和颜色j不同的颜色就为{(j+1)%3,(j+2)%3};
参考代码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[1010][3],R[1010],G[1010],B[1010],ans,t,icase;
int main()
{
scanf("%d",&t);
while (t--)
{
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d %d %d",&R[i],&G[i],&B[i]);
memset(dp,0,sizeof(dp));
for(int i=1; i<=n; i++)
{
dp[i][0]=min(dp[i-1][1],dp[i-1][2])+R[i];
dp[i][1]=min(dp[i-1][0],dp[i-1][2])+G[i];
dp[i][2]=min(dp[i-1][0],dp[i-1][1])+B[i];
}
ans=min(dp[n][0],min(dp[n][1],dp[n][2]));
printf("Case %d: %d\n",++icase,ans);
}
return 0;
}
题意:给定一个数组,查询给定区间内的最小值。
题解:线段树求区间最值。。。可能学名叫RMQ?
参考代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int tre[400010],t,n,m,icase;
void build(int rt,int l,int r)
{
if (l==r)
{
scanf("%d",&tre[rt]);
return;
}
int mid=(l+r)/2;
build(rt*2,l,mid);
build(rt*2+1,mid+1,r);
tre[rt]=min(tre[rt*2],tre[rt*2+1]);
}
int query(int rt,int l,int r,int L,int R)
{
if(l>=L && r<=R)
{
return tre[rt];
}
int mid=(l+r)/2,ret=1000000000;
if (mid>=L)
{
ret=min(query(rt*2,l,mid,L,R),ret);
}
if (mid<R)
{
ret=min(query(rt*2+1,mid+1,r,L,R),ret);
}
return ret;
}
int main()
{
scanf("%d",&t);
while (t--)
{
printf("Case %d:\n",++icase);
scanf("%d%d",&n,&m);
build(1,1,n);
while (m--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",query(1,1,n,l,r));
}
}
return 0;
}
题意:给出一棵树,n个顶点,n-1 条边,给出每条边的权值,问树上的任意两点的最大距离是多少。
题解:树上最长路?用“树的直径”做。先确定起点,随后从这个起点再次遍历整棵树,便能更新出整棵树上的最大距离了。用bfs遍历即可。
附上树的直径详解博客http://www.cnblogs.com/wuyiqi/archive/2012/04/08/2437424.html
参考代码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int edgenum,head[300010],sx,ans,dist[300010],vis[300010],t,icase,n;
struct node
{
int to,val,next;
}edge[300010];
void add(int u,int v,int w)
{
node tp={v,w,head[u]};
edge[edgenum]=tp;
head[u]=edgenum++;
}
void bfs(int st)
{
memset(dist,0,sizeof(dist));
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(st);
dist[st]=0;
vis[st]=1;
ans=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(!vis[v])
{
vis[v]=1;
dist[v]=dist[u]+edge[i].val;
if(ans<dist[v])
{
ans=dist[v];
sx=v;
}
q.push(v);
}
}
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
edgenum=0;
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=0; i<n-1; i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);add(b,a,c);
}
bfs(0);
bfs(sx);
printf("Case %d: %d\n",++icase,ans);
}
return 0;
}