• AtCoder Grand Contest 002


    AtCoder Grand Contest 002

    A - Range Product

    翻译

    告诉你(a,b),求(prod_{i=a}^b i)是正数还是负数还是零。

    题解

    什么鬼玩意。

    #include<iostream>
    using namespace std;
    int main()
    {
    	int a,b;
    	cin>>a>>b;
    	if(a<=0&&b>=0)cout<<"Zero"<<endl;
    	else
    	{
    		if(a>0)cout<<"Positive"<<endl;
    		else if((b-a+1)%2)cout<<"Negative"<<endl;
    		else cout<<"Positive"<<endl;
    	}
    	return 0;
    }
    

    B - Box and Ball

    翻译

    (n)个盒子,(1)号盒子里有一个红球,其他盒子里有一个白球,(m)个操作,每次随机将(x_i)中的一个球放进(y_i)中,求所有可能存在红球的盒子数量。

    题解

    什么鬼玩意。暴力模拟一下没事了。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 100100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    bool vis[MAX];
    int n,m,ans,a[MAX];
    int main()
    {
    	n=read();m=read();
    	vis[1]=true;
    	for(int i=1;i<=n;++i)a[i]=1;
    	while(m--)
    	{
    		int x=read(),y=read();
    		if(!a[x])continue;
    		if(vis[x])vis[y]=true;
    		--a[x];++a[y];
    		if(!a[x])vis[x]=false;
    	}
    	for(int i=1;i<=n;++i)ans+=vis[i];
    	printf("%d
    ",ans);
    	return 0;
    }
    

    C - Knot Puzzle

    翻译

    (n)根绳子,第(i)根的长度为(a_i),一开始所有绳子都被系在一起成了一条链,每次你可以选择根长度不小于(L)的绳子,然后解开它的任意一个结,问能否解开所有结,如果可以输出一个方案。

    题解

    诶,这种题目我都不会做,菜的不行啊。
    我们只考虑最后一次操作,显然是要断开两个连在一起的绳子,那么它们必定相邻,并且和大于等于(L),所以找到这个位置之后把前后顺次解完,最后再解这个位置就好了。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define MAX 100100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,L,a[MAX];
    int main()
    {
    	n=read();L=read();
    	for(int i=1;i<=n;++i)a[i]=read();
    	bool fl=false;int pos;
    	for(int i=1;i<n;++i)if(a[i]+a[i+1]>=L)fl=true,pos=i;
    	if(!fl){puts("Impossible");return 0;}
    	puts("Possible");
    	for(int i=1;i<pos;++i)printf("%d
    ",i);
    	for(int i=n-1;i>pos;--i)printf("%d
    ",i);
    	printf("%d
    ",pos);
    	return 0;
    }
    

    D - Stamp Rally

    翻译

    给定一张图,每次询问从(x-y)选择两条路径,要求被经过的点数恰好为(z),求经过的最大编号的边的最小值。

    题解

    最大编号的最小值( ightarrow)二分,多组询问( ightarrow)整体二分。
    怎么二分?两个点如果联通,答案就是联通块大小,否则就是两个点所在的联通块大小之和。
    怎么维护联通块大小?并查集,反正支持撤销,搞定啦。
    yyb因为把一个m打成了n而调了40分钟

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define MAX 100100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    struct Ask{int x,y,z,id;}p[MAX],tmp[MAX];
    struct edge{int x,y;}e[MAX],S[MAX];
    int sz[MAX],f[MAX],top,ans[MAX];
    int getf(int x){while(x!=f[x])x=f[x];return x;}
    int n,m,Q;
    void Work(int l,int r,int L,int R)
    {
    	if(l==r){for(int i=L;i<=R;++i)ans[p[i].id]=l;return;}
    	int mid=(l+r)>>1,stop=top,t1=L-1,t2=R+1;
    	for(int i=l;i<=mid;++i)
    	{
    		int u=getf(e[i].x),v=getf(e[i].y);
    		if(u==v)continue;
    		if(sz[u]<sz[v])swap(u,v);
    		f[v]=u;sz[u]+=sz[v];S[++top]=(edge){u,v};
    	}
    	for(int i=L;i<=R;++i)
    	{
    		int x=getf(p[i].x),y=getf(p[i].y),tot;
    		if(x==y)tot=sz[x];else tot=sz[x]+sz[y];
    		if(tot>=p[i].z)tmp[++t1]=p[i];
    		else tmp[--t2]=p[i];
    	}
    	for(int i=L;i<=R;++i)p[i]=tmp[i];
    	Work(mid+1,r,t2,R);
    	while(top>stop)
    	{
    		int u=S[top].x,v=S[top].y;--top;
     		f[v]=v;sz[u]-=sz[v];
    	}
    	Work(l,mid,L,t1);
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=m;++i)e[i].x=read(),e[i].y=read();
    	for(int i=1;i<=n;++i)f[i]=i,sz[i]=1;
    	Q=read();
    	for(int i=1;i<=Q;++i)
    	{
    		int x=read(),y=read(),z=read();
    		p[i]=(Ask){x,y,z,i};
    	}
    	Work(1,m,1,Q);
    	for(int i=1;i<=Q;++i)printf("%d
    ",ans[i]);
    	return 0;
    }
    

    E - Candy Piles

    翻译

    (n)堆石子,第(i)堆有(a_i)个石子。有两种操作:

    • 把石子最多的那一堆给丢掉
    • 把每一堆全部丢掉一个

    谁拿走最后石子谁输。判断胜负情况。

    题解

    这样考虑,把所有(a_i)从大往小排序,这样子我们的两个操作可以看做在一个不规则的棋盘上走,其中(x=i)的上边界是(y=a_i),一开始位于((0,0)),第一个操作等价于向右走一步,第二个操作等价于向上走一步。谁走到了边界谁输。然而还是不知道怎么判断胜负情况,可以打一个表,发现((x,y))这个位置的胜负情况等于((x+1,y+1))的胜负情况,那么直接找到最大的合法((x,x))位置然后暴力判断一下就行了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define MAX 100100
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,a[MAX],ans;
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)a[i]=read();
    	sort(&a[1],&a[n+1]);reverse(&a[1],&a[n+1]);
    	for(int i=1;i<=n;++i)
    		if(i+1>a[i+1])
    		{
    			for(int j=i+1;a[j]==i;++j)ans^=1;
    			ans|=(a[i]-i)&1;
    			ans?puts("First"):puts("Second");
    			return 0;
    		}
    	return 0;
    }
    

    F - Leftmost Ball

    翻译

    (n)种颜色的球,每种(K)个,现在把他们排成一行,把每种颜色的最靠左的球给染成第(0)种颜色。
    求最终序列的方案数。

    题解

    我们从前往后依次把每个颜色按顺序来放,那么如果当前放的是某种颜色的第一个球,那么放的就会变成(0)号颜色,所以无论何时,(0)号颜色的数量不能少于其他颜色的数量。

    可以设状态(f[i][j])表示前面一共放了(i)(0)号颜色的球,而一共出现了(j)种其他颜色的球,根据上面的东西,可以知道(ige j)。每次转移我们分成两种考虑。第一种就直接在后面接一个(0)号颜色的球,这个不需要考虑任何决策,直接转移即可,也就是(f[i][j]+=f[i-1][j])。另外一种转移是选择一共新的颜色,抛去前面已经放好的(0)号颜色的前,抛去当前位置放下一个当前颜色的球,那么还需要在后面选择(k-2)个位置来放这些球,而后面剩下的空位个数显然是可以算的。首先还剩下(n-i)(0)号颜色的球没有放,所以提供(n-i)个空位,前面一共只出现了(j-1)种颜色,所以还有((n-j+1)*(k-1))个空位,但是当前的位置被钦定放这种新的颜色,所以还要减少一个位置。

    也就是转移长成这个样子:

    [f[i][j]=f[i-1][j]+C_{(n-i)+(n-j+1)*(k-1)-1}^{k-2}*f[i][j-1]*(n-j+1) ]

    时间复杂度(O(nk))

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define ll long long
    #define MAX 2010
    #define MOD 1000000007
    void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,k,mx,f[MAX][MAX];
    int jc[MAX*MAX],jv[MAX*MAX],inv[MAX*MAX];
    int C(int n,int m){return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
    int main()
    {
    	n=read();k=read();mx=n*k;
    	if(k==1){puts("1");return 0;}
    	jc[0]=jv[0]=inv[0]=inv[1]=1;
    	for(int i=1;i<=mx;++i)jc[i]=1ll*jc[i-1]*i%MOD;
    	for(int i=2;i<=mx;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=1;i<=mx;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
    	f[0][0]=1;
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<=i;++j)
    		{
    			add(f[i][j],f[i-1][j]);
    			if(j)add(f[i][j],1ll*f[i][j-1]*(n-j+1)%MOD*C((n-i)+(n-j+1)*(k-1)-1,k-2)%MOD);
    		}
    	printf("%d
    ",f[n][n]);
    	return 0;
    }
    
  • 相关阅读:
    纷享销客公司产品能力学习笔记
    有质量的两道面试题
    java项目启动时执行指定方法
    css银行卡号样式
    swiper6使用鼠标滚轮失效退回swiper4即可
    vue-swiper Demo
    vue点击下载图片
    跨域请求发送数据在body里,java后台接收
    跨域,跨服务session获取不到,前后台不会传输Cookie,sessionId不一致
    Windows下 redis命令及配置
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9640852.html
Copyright © 2020-2023  润新知