ACM-ICPC 2017 Asia Urumqi (solve 6/11)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/passer__/article/details/81938619

题目链接:https://www.jisuanke.com/contest/1409?view=challenges

A题

题意:给你n个硬币,m次操作,每次抛上去k个硬币,问你m次操作后硬币向上的次数的期望。

思路:当有至少 k 枚硬币面朝下时,则选 k 枚面朝下的硬币去抛掷(任意k 枚都可以);如果不足 k 枚面朝下,则在选择所有面朝下的硬币的基础上再额外选择若干面朝上的银币。

dp[i][j] 是第i次j个向上的概率。代码有注释,详情看代码。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
double dp[105][105];
double C[105][105];
double fac[105];
void init()//预处理组合数 
{
    C[0][0]=1;
    for(int i=1;i<105;i++)
        for(int j=0;j<=i;j++)
            C[i][j]=(j == 0) ? 1:(C[i-1][j]+C[i-1][j-1]);
    fac[0] = 1;
    for(int i = 1;i <= 100;i++) //预处理次数 
		fac[i] = fac[i-1]*0.5;
}
int main()
{ 
	init();
    int t,n,m,i,j,k,x;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&x);
       	memset(dp,0,sizeof(dp));
        dp[0][0] = 1.0;
        for(i = 0;i < m;i++)//m次 
        {
            for(j = 0;j <= n;j++)//j个朝上的
            {
                for(k = 0;k <= x;k++)//抛上去的k个中有多少个下来是正面的 
                {
                    if(n - j >= x) //都从朝下的里面选k个朝上 
						dp[i+1][j+k] += dp[i][j]*C[x][k]*fac[x];
                    else //需要从在上的里面选 
						dp[i+1][k+n-x] += dp[i][j]*C[x][k]*fac[x];
                }//dp[i][j] 抛掷i次j个朝上的概率 
            }
        }
        double ans = 0;
        for(i = 0;i <= n;i++)//期望 
			ans = ans + dp[m][i]*i;
        printf("%.3f\n",ans);
    }
    return 0;
}

B题

题意:给你4个数求一个max(a*b-c*d)

思路:sort一下就行。

D题

题意:给你个圆和k个点,问你最多分割多少块。

思路:结论题,蓝书123页。

G 题

题意:给你n个点,构成一个山峰的形状,保证第一个点事原点,保证最后一个点一定落在X轴。

思路:套一下求多边形面积公式即可。自己也可以百度下。

I题

题意:给你一棵树,然后给你问你树上两点(通过唯一的路可达)之间是否可以异或成他给的那个值,两点之间的边权随意设定。给你c个条件,问你前多少个条件一定可以满足。

思想:用带权并查集解决这一类问题,对于一些莫名的路径,先用并查集将连续的区间连接起来,如果查询的区间属于一个大的联通块的话,只需要查询下他们到共同的一个节点的sum1^sum2是否等于他们需要的valu,因为共同的异或之后就是0了,所以的到的就是之间的值,对于是2个联通块的话,需要将2个连接成一个,将他们2个公共点相连,而且知道他们之间的值,便可以知道另外一条边的值,如下图。

对于已知a,b,c的话,那么d来说就等于,a^b^c。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int fa[100005];
ll valu[100005];
int find(int x)
{
    if(x==fa[x])
		return x;
    int fx=fa[x];
    fa[x]=find(fa[x]);
    valu[x]^=valu[fx]; //从底到上的反过来 
    return fa[x];
}
int check(int x,int y,ll val)
{
    int fx=find(x);
    int fy=find(y);
    if(fx==fy)
		return 0;
    valu[fx]^=valu[x]^valu[y]^val; //如果异或的部分 
    fa[fx]=fy;
    return 1;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,c,u,v;
		ll Valu;
		scanf("%d%d",&n,&c);
		for(int i=0;i<n-1;i++)
			scanf("%d%d",&u,&v);
		for(int i=1;i<=n;i++)
		{
			fa[i]=i;
			valu[i]=0;
		}
		int ans=c;
		for(int i=0;i<c;i++)
		{
			scanf("%d%d%lld",&u,&v,&Valu);
			if(flag && check(u,v,Valu)==0  && (valu[u]^valu[v])!=Valu)//同一个区间而且不等 
				ans=i;
		}
		printf("%d\n",ans);
	}
	return 0;
} 

K题

题意:给你个T表的规则,就是对于1 ≤ r and 1 ≤ c ≤ r. 如果gcd(r,c)== 1, Tr,c = c, 否则等于0。S表就是对于某一行来说求当前点到最后的一个对于T表的后缀和。然后给你个K求出来K行的一个S表的和。

官方题解:

个人思路:假设当前是个素数的话,你会发现除了最后一个都是c,这样对于每个数的贡献是1^2+2^2+3^2----------(n-1)^2

因为对于每个点他只能贡献他的位置的次数,比如5 他会给1 2 3 4 5 这五个位置贡献,所以就是5*5 就是5^2

然后考虑删去那些等于0的位置产生的贡献,这些位置肯定是K的素因子和它这些因子的倍数。对于1e8来说,最多不会超过9个素因子,因此枚举这些因子产生的子集即可,枚举子集时间复杂度最大是2^9。剩下就是容斥原理的东西了,对于1^2+2^2+-----k^2,有一个公式可以计算(1/6)n(n+1)(2n+1)  推导过程:https://zhidao.baidu.com/question/87353666.html

对于某个素因子的倍数来说,提取这个素因子*素因子,例如10 2的话就是2^2+4^2+6^2+8^2  提取2^2(1^2+2^2+3^2+4^2)同样可以用那个公式。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
const ll mod = 998244353;
int yin[maxn];
ll inv(ll b){return b==1?1:(mod-mod/b)*inv(mod%b)%mod;}
ll Cal(ll x,ll n)
{
	ll k=(n-x)/x+1;//计算除去当前有多少  提取x^2 之后是(1^2+2^2----) 
	return k*(k+1)%mod*(2*k%mod+1)%mod*inv(6)%mod*x%mod*x%mod;
}
int main()
{
	ll t,k;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&k);
		ll n=k;
		int top=0;
		for(int i=2;i*i<=k;i++)//求因子 
		{
			if(k%i==0)	
			{
				yin[top++]=i;
				while(k%i==0)
					k=k/i;
			}
		}
		if(k>1)
			yin[top++]=k;
		ll ans=0;
		for(int i=1;i<(1<<top);i++)
		{
			int Sum=0;
			int Ans=1; 
			for(int j=0;j<top;j++)//枚举素因子 
			{
				if((1<<j)&i)//枚举子集 
				{
					Sum++;//控制加还是减 
					Ans=Ans*yin[j];//计算从哪个数开始 
				} 
			} 
			ll temp=Cal(Ans,n);
			if(Sum&1)//奇数
				ans=(ans+temp)%mod;
			else
				ans=(ans-temp+mod)%mod;
		}
		ans=(Cal(1,n)-ans+mod)%mod;//1^2+2^2+3^2------(n-1)^2 
		printf("%lld\n",ans);
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/passer__/article/details/81938619