集训队例赛——20111009 解题报告

<pre name="code" class="cpp">//先上代码,注释稍后奉上。。。


/*
1010
典型线段树
*/
#include <stdio.h>
#include <iostream>
#include <string.h>
#include<queue>
#include<cmath>
using namespace std;
struct Tree
{
	int L,R,v;
	int flag;
}tree[100000*4];
void create(int t,int L,int R)
{
	tree[t].L=L;
	tree[t].R=R;
	tree[t].flag=0;
	int mid=(L+R)>>1;
	if(L==R)
	{tree[t].v=1;return ;}
		create(2*t,L,mid);
		create(2*t+1,mid+1,R);
	tree[t].v=tree[2*t].v+tree[2*t+1].v;
}
void down(int t)//释放松弛
{

	if(tree[t].flag==0)return ;
	tree[2*t].v=(tree[2*t].R-tree[2*t].L+1)*(tree[t].v/(tree[t].R-tree[t].L+1));
	tree[2*t+1].v=(tree[2*t+1].R-tree[2*t+1].L+1)*(tree[t].v/(tree[t].R-tree[t].L+1));
	tree[2*t].flag=1;
	tree[2*t+1].flag=1;
	tree[t].flag=0;
}
void up(int t)
{
	if(tree[t].flag)return ;
	tree[t].v=tree[2*t].v+tree[2*t+1].v;
}
void update(int t,int L,int R,int v)
{
	//cout<<t<<"#"<<endl;
	int mid=(tree[t].L+tree[t].R)>>1;
	if(tree[t].L==L&&tree[t].R==R)
	{
		tree[t].v=(tree[t].R-tree[t].L+1)*v;
		tree[t].flag=1;
		up(t);
		return ;
	}
	down(t);
	if(R<=mid)
	{
		update(2*t,L,R,v);
	}
	else if(L>mid)
	{
		update(2*t+1,L,R,v);
	}
	else 
	{
		update(2*t,L,mid,v);
		update(2*t+1,mid+1,R,v);
	}
	up(t);
}
int n,m,x,y,k;
int main()
{
	int ca;
	scanf("%d",&ca);
	for(int ii=1;ii<=ca;ii++)
	{
		scanf("%d",&n);
		create(1,1,n);
		scanf("%d",&m);
		while(m--)
		{
			scanf("%d%d%d",&x,&y,&k);
			update(1,x,y,k);
		}
		printf("Case %d: The total value of the hook is %d.\n",ii,tree[1].v);
	}
    return 0;
}

/*
1009
水题,不解释
*/
#include <cstdio>
#include <iostream>
#include <memory.h>
#include <cmath>
using namespace std;

int vis[90];
int main () {
	int n,tmp;
	while(scanf("%d",&n),n)
	{
		memset(vis,0,sizeof(vis));
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<6;j++)
			{scanf("%d",&tmp);
			vis[tmp]=1;}
		}
		int flag=0;
		for(int i=1;i<50;i++)
		if(!vis[i]){flag=1;break;}
		if(flag)puts("No");
		else puts("Yes");
		//puts("");
	}
}


/*
1002
 穿马甲,带删点的并查集
 关键是要把一个元素从集合中取出来。
 我们并不关心集合中到底有哪些元素
 取出一个元素i可以看成新加了一个点x
 把v[i]对应x就ok了
 注意如果一个集合的元素全被抽取出来的情况,这个时候集合数量是减少的
 判断这种情况只需要记录集合的大小就行了
 v[i]=num表示标号为i的点对应于实际的点
*/
#include <cstdio>
#include <iostream>
#include <memory.h>
#include <cmath>
using namespace std;

int fa[2000009];
int v[2000009];
int r[2000009];
int find(int x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void Un(int x,int y)
{
	x=find(x);
	y=find(y);
	if(x==y)return ;
	fa[x]=y;
	r[y]+=r[x];
}
int main () {
	int n,m;
	char op[2];
	int ca=1;
	int x,y;
	while(scanf("%d%d",&n,&m),n+m)
	{
		int num=n;
		for(int i=0;i<n;i++)
		{
			v[i]=i;
			fa[i]=i;
			r[i]=1;//集合的大小
		}
		while(m--)
		{
			scanf("%s",op);
			if(op[0]=='M')
			{
				scanf("%d%d",&x,&y);
				Un(v[x],v[y]);//查找也是马甲的代表元,好好体会。。。
			}
			else 
			{
				scanf("%d",&x);
				fa[num]=num;
				r[num]=1;//新增一个点num
				r[find(v[x])]--;//被抽取集合的数量减1
				v[x]=num++;//注意对应关系,其实就是穿了一层马甲
			}
		}
		printf("Case #%d: ",ca++);
		int ans=0;
		for(int i=0;i<num;i++)
		{
			if(fa[i]==i&&r[i]>0)
			ans++;
		}
		printf("%d\n",ans);
	}
}

/*
1004
水题,不解释
可参考coj  1037
*/
#include<iostream>
#include<vector>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
double s[20009],p[20009];
int T,n;
double k;
int main()
{
	int ca=1;
	while(scanf("%d%d%lf",&T,&n,&k),T+n+k)
	{
		double sum=0;
		for(int i=0;i<T;i++)
		{
			scanf("%lf%lf",&s[i],&p[i]);
			sum+=(1-p[i]/100.0)*s[i];
		}
		printf("Case %d: %.2lf\n",ca++,sum/k);
		puts("");
	}
   return 0;
}

/*
1008
关键是怎么算出相交的面积,具体看注释
*/
#include<iostream>
#include<vector>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
double x[30],y[30],r[30];
const double PI=acos(-1.0);
const double eps=1e-9;
int n;
double dis(int i,int j)
{
	return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
bool check(double mid,int x)
{
	for(int i=0;i<n;i++)if(i!=x)
	{
		double D=dis(x,i);
		if(mid<D)return false;//mid值没有到达测试圆的圆心,肯定达不到1/2;
		if(mid>D+r[i])continue;//把整个圆包含了,不必再考虑
		double lc=acos((r[i]*r[i]+D*D-mid*mid)/(2.0*r[i]*D));
		double s1=lc*r[i]*r[i];//测试圆扇形的面积
		double ld=acos((mid*mid+D*D-r[i]*r[i])/(2.0*D*mid));
		double s2=ld*mid*mid;//生成圆扇形的面积
		double s3=s2-r[i]*D*sin(lc);//生成圆扇形的面积-两个三角形的面积
		if(s3+s1<PI*r[i]*r[i]/2.0)return false;
	}
	return true;
}
double solve(int x)
{
	double L=sqrt(2.0)/2.0*r[x],R=10009,mid;
	while(R-L>eps)
	{
		//cout<<R<<endl;
		mid=(R+L)/2.0;
		if(check(mid,x))R=mid;
		else L=mid;
	}
	return R;
}
int main()
{
	int ca;
	scanf("%d",&ca);
	while(ca--)
	{
	//cout<<PI<<endl;
		scanf("%d",&n);
		for(int i=0;i<n;i++)
		{
			scanf("%lf%lf%lf",&x[i],&y[i],&r[i]);
		}
		double ans=1000009;
		for(int i=0;i<n;i++)
		{
			double tmp=solve(i);
			if(tmp<ans)
			ans=tmp;
		}
		printf("%.4lf\n",ans);
	}
   return 0;
}

/*
1001
贪心加剪枝
*/
#include<iostream>
#include<vector>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int in[1009];
int main()
{
int ca,n;
scanf("%d",&ca);
while(ca--)
{
	scanf("%d",&n);
	int flag=1,sum=0;
	for(int i=0;i<n;i++)
	{
		scanf("%d",&in[i]);
		if(in[i]>n)flag=0;
		sum+=in[i];
	}
	if(sum&1)flag=0;
	if(!flag)
	puts("no");
	else 
	{
		sort(in,in+n);
		for(int i=0;i<n;i++)//将最大入度的点和其他点构造边
		{
			for(int j=n-1;j>i&&in[i]>0;j--)
			{
				if(in[j]>0)
				{in[j]--;in[i]--;}
			}
			if(in[i]>0)break;
			sort(in,in+n);//每次取最大
		}
		for(int i=0;i<n;i++)
		if(in[i]!=0){flag=0;break;}
		if(!flag)puts("no");
		else puts("yes");
	}
}
return 0;
}

/*
1005
数学题,其实就是均匀分布
*/
#include <algorithm>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <deque>
#include <stack>
#include <fstream>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <list>
#include <stdexcept>
#include <functional>
#include <utility>
#include <ctime>
using namespace std;
int main()
{
    int n,D,ca=1;
    while (scanf("%d%d",&n,&D),n+D)
    {
        int i,j,k;
        double ans=D;
        for (i=0;i<n;i++)
        {
            double p,L,v;
            scanf("%lf%lf%lf",&p,&L,&v);
            ans-=L;
            ans+=2*L/v;//过河的时间期望
        }
        printf("Case %d: %.3lf\n\n",ca++,ans);
    }
    return 0;
}


/*
1007
大模拟
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<map>
using namespace std;
int n,T,m;
int k,S;
int Serv[109];
int isOpen[109][1009];
int trans[109][109];
int work[109];
int now[109];
void init()
{
    int tmp,a,b;
    scanf("%d%d",&n,&T);
    scanf("%d%d",&k,&S);
    memset(Serv,0,sizeof(Serv));//标记是否为服务器
    memset(isOpen,0,sizeof(isOpen));//isOpen[i][j]表示机器i在j时间是否开机
    memset(now,0,sizeof(now));//机器i当前下载的量
    memset(work,-1,sizeof(work));//机器i什么时候开始工作
    for(int i=1;i<=k;i++)
    {
        scanf("%d",&tmp);
        Serv[tmp]=1;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        scanf("%d",&trans[i][j]);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&tmp);
        while(tmp--)
        {
            scanf("%d%d",&a,&b);
            for(int j=a;j<b;j++)
            isOpen[i][j]=1;//对于机器i,时间a到b是开机的
        }
    }
    scanf("%d",&m);
    while(m--)
    {
        scanf("%d%d",&tmp,&a);
        work[a]=tmp;
    }
}
void solve()
{
    for(int i=0;i<T;i++)
    {
        for(int j=1;j<=n;j++)//非服务器j从服务器k下载
        {
            if(!Serv[j])continue;

            if(!isOpen[j][i])continue;
            for(int k=1;k<=n;k++)
            {
                if(i<work[k])continue;
                if(!isOpen[k][i])continue;
                if(Serv[k])continue;
                now[k]+=trans[j][k];

            }
        }
        for(int j=1;j<=n;j++)//没过1s,将成为服务器的标记
        if(now[j]>=S)
       Serv[j]=1;
    }
    for(int i=1;i<=n;i++)
    {
        if(Serv[i])puts("100%");
        else
        {
            double ans=100*now[i]*1.0/S;
            printf("%d%%\n",(int)ans);
        }
    }
}
int main()
{
    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
        init();
        solve();
    }
return 0;
}

/*1006
树形DP
dreamfox版
*/
#define M 100005
struct G
{
    int u,w;
};
int dp[M],f[M];//dp[x]表示离x的最远节点的距离,f[x]表示最远节点路径上的最近节点标号
int dp2[M],f2[M];//dp2[x]表示离x的次远节点的距离,f[x]表示次远节点路径上的最近节点标号
int Max1,Max2;
int p1,p2,n;
vector<G> v[M];
void dfs(int pre,int fa,int deg)
{
    int i,j;
    for (i=0;i<v[fa].size();i++)
    {
        int x=v[fa][i].u;
        if (x==pre) continue;
        dfs(fa,x,deg+1);
        if (dp[x]+v[fa][i].w>dp[fa])
        {
            if (dp[fa]>dp2[fa])
            {
                dp2[fa]=dp[fa];
                f2[fa]=f[fa];
            }
            dp[fa]=dp[x]+v[fa][i].w;
            f[fa]=x;
        }
        else if (dp[x]+v[fa][i].w>dp2[fa])
        {
            dp2[fa]=dp[x]+v[fa][i].w;
            f2[fa]=x;
        }
        //dp[fa]=max(dp[fa],dp[x]+v[fa][i].w);
    }
}
void dfs2(int pre,int fa,int len)
{
    int i,t;
    if (fa!=1)
    {
        if (fa==f[pre])
        {
            if (dp[fa]<dp2[pre]+len)
            {
                dp2[fa]=dp[fa];
                f2[fa]=f[fa];
                dp[fa]=dp2[pre]+len;
                f[fa]=pre;
            }
            else if (dp2[fa]<dp2[pre]+len)
            {
                dp2[fa]=dp2[pre]+len;
                f2[fa]=pre;
            }
        }
        else
        {
            if (dp[fa]<dp[pre]+len)
            {
                dp2[fa]=dp[fa];
                f2[fa]=f[fa];
                dp[fa]=dp[pre]+len;
                f[fa]=pre;
            }
            else if (dp2[fa]<dp[pre]+len)
            {
                dp2[fa]=dp[pre]+len;
                f2[fa]=pre;
            }
        }
    }
    for (i=0;i<v[fa].size();i++)
    {
        int x=v[fa][i].u;
        if (x==pre) continue;
        dfs2(fa,x,v[fa][i].w);
    }
}
int main()
{
    while (scanf("%d",&n)!=EOF)
    {
        int i,j;
        for (i=0;i<=n+1;i++) v[i].clear();
        memset(dp,0,sizeof(dp));
        memset(dp2,0,sizeof(dp2));
        memset(f,0,sizeof(f));
        memset(f2,0,sizeof(f2));
        for (i=2;i<=n;i++)
        {
            int u,w;
            G t;
            scanf("%d%d",&u,&w);
            t.u=u;
            t.w=w;
            v[i].push_back(t);
            t.u=i;
            v[u].push_back(t);
        }
        if (n==1)
        {
            printf("0\n");
            continue;
        }
        dfs(-1,1,0);
        dfs2(-1,1,0);
        for (i=1;i<=n;i++)
            printf("%d\n",dp[i]);
    }
    return 0;
}

//胖华版
//好吧是我的错,加错题了,这是一道经典树状dp,两次dfs,或一次dfs,一次bfs都可以,记录最大的和次小的
#include <cstdio>
#include <iostream>
#include <memory.h>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAX=10110;

int n;
int head[MAX];
int qx[MAX], qp[MAX];
int m1[MAX], m2[MAX], s1[MAX], s2[MAX];
int l;
int front, rear;

struct node {
    int e, next, v;
}edge[MAX*4];

void add_edge(int s, int e, int v) {
    edge[l].e =e;
    edge[l].next =head[s];
    edge[l].v =v;
    head[s]=l++;
}

int dfs(int x, int pre) {
    int l1;
    for(int i=head[x];i!=-1;i=edge[i].next ) {
        int e=edge[i].e ;
        if(e==pre)
            continue;
        l1=dfs(e, x)+edge[i].v ;
        if(l1>m1[x]) {
            m2[x]=m1[x], s2[x]=s1[x];
            m1[x]=l1, s1[x]=e;
        }
        else if(l1>m2[x]) {
            m2[x]=l1, s2[x]=e;
        }
    }
    return m1[x];
}

void bfs() {
    int x, pre;
    front=rear=0;
    qx[rear]=1;
    qp[rear++]=-1;
    while(front<rear) {
        x=qx[front];
        pre=qp[front++];
        for(int i=head[x];i!=-1;i=edge[i].next ) {
            int e=edge[i].e ;
            if(e==pre)
                continue;
            int val=edge[i].v ;
            if(s1[x]==e) {
                int t=m2[x]+val;
                if(t>m1[e]) {
                    m2[e]=m1[e], s2[e]=s1[e];
                    m1[e]=t, s1[e]=x;
                }
                else if(t>m2[e]) {
                    m2[e]=t, s2[e]=x;
                }
            }
            else {
                int t=m1[x]+val;
                if(t>m1[e]) {
                    m2[e]=m1[e], s2[e]=s1[e];
                    m1[e]=t, s1[e]=x;
                }
                else if(t>m2[e]) {
                    m2[e]=t, s2[e]=x;
                }
            }
            qx[rear]=e;
            qp[rear++]=x;
        }
    }
}

void init() {
    int a, b;
    l=0;
    memset(head, -1, sizeof(head));
    memset(m1, 0, sizeof(m1));
    memset(m2, 0, sizeof(m2));
    for(int i=2;i<=n;i++) {
        scanf("%d %d", &a, &b);
        add_edge(i, a, b);
        add_edge(a, i, b);
    }
}

void solve() {
    init();
    dfs(1, -1);
    bfs();
    for(int i=1;i<=n;i++)
        printf("%d\n", m1[i]);
}

int main () {
    while(scanf("%d", &n)!=EOF)
        solve();
    return 0;
}







  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值