2020牛客暑期多校训练营(第三场)

A Clam and Fish(贪心)

题意:

  • 0 0 0 阶段是没有鱼也没有蛤
  • 1 1 1 阶段是没有鱼有蛤
  • 2 2 2 阶段是有鱼没有蛤
  • 3 3 3 阶段是有鱼也有蛤

蛤可以制作成鱼饵,来获取鱼,但是在有蛤的时候需要制作成鱼饵在下一阶段才能使用,且直接有鱼的情况下,不需要用鱼饵也可以获取鱼。制作鱼饵和直接钓鱼在一个阶段只能选择一项来进行。求最多可以获得多少条鱼。

有鱼的天数就抓一条鱼,光有蛤的时候可以选择这一阶段做鱼饵还是在这一天用鱼饵钓一条鱼。遍历一遍, 0 0 0 的时候有鱼饵就用鱼饵钓鱼, 2 , 3 2,3 2,3 都是钓鱼。 1 1 1 的时候都做鱼饵,最后把多的鱼饵 / 2 /2 /2 加上就行。

AC代码:

#include
using namespace std;
 
const int mxn=2000010;
int n;
char s[mxn];
 
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        scanf("%s",s);
        int ans=0,nw=0;
        for(int i=0;i<n;i++){
            int a=s[i]-'0';
            if(a&2){
                ans++;
                continue;
            }
            if(a==1){
                nw++;
            }
            else{
                if(nw==0) continue;
                nw--;
                ans++;
            }
        }
        ans+=nw/2;
        printf("%d\n",ans);
    }
    return 0;
}

B Classical String Problem(找规律)

题意;

给你一个字符串,下标 1 1 1 开头, q q q 组询问,每一组有一个字符 o p op op,一个数字 x x x o p = ′ A op='A op=A ’时,输出当前字符串第 x x x个字符, o p = ′ M ’ op='M’ op=M 时,如果 x > 0 x>0 x>0 ,把字符串最左边的 x x x 个字符放到字符串右边, x < 0 x<0 x<0,把字符串最右边 ∣ x ∣ |x| x 个字符放到字符串左边。

o d e r n o w c odernowc odernowc
o w c o d e r n owcodern owcodern
w c o d e r n o wcoderno wcoderno

因为每次变的只有前缀和后缀,所以每次记录一下往后面或者往前面挪了多少,输出的时候直接在原串输出就行了,因为 m o d i f y modify modify 的时候串的顺序是不变的。

AC代码:

const int N = 2e6 + 50;
int n, m;
char s[N];
int main()
{
	ss(s);
	sd(m);
	int len=strlen(s);
	int pos = 0;
	while (m--)
	{
		char op;
		int x;
		getchar();
		scanf("%c %d",&op,&x);
		if (op == 'M')
		{
			if (x > 0)
			{
				pos += x;
				pos %= len;
			}
			else
			{
				pos += x;
				pos += len;
				pos %= len;
			}
		}
		else
		{
			printf("%c\n", s[(x + pos - 1 + len) % len]);
		}
	}
	return 0;
}

C Operation Love(计算几何)

题意:

给你2个手,左手和右手,相互对称,给你右手的图,然后给你20个连续的点(可顺可逆)要你判断是右手还是左手。

因为不改变手掌的大小,值改变手掌的位置和方向,所以只需要找到相邻且没重复的两条边,用叉积判断他们的位置关系即可判断是左手还是右手。

AC代码:

const int mxn = 1010;
const double eps = 1e-3;
 
struct po
{
    double x, y;
} a[mxn];
po operator-(po a, po b)
{
    return (po){a.x - b.x, a.y - b.y};
}
double len(po a)
{
    return sqrt(a.x * a.x + a.y * a.y);
}
bool dengyu(double x, double y)
{
    return abs(x - y) < eps;
}
int n = 20;
 
double cro(po a, po b)
{
    return a.x * b.y - b.x * a.y;
}
bool sol()
{
    for (int i = 0; i < n; i++)
    {
        scanf("%lf%lf", &a[i].x, &a[i].y);
    }
    a[n] = a[0];
    a[n + 1] = a[1];
    for (int i = 0; i <= n; i++)
    {
        po p = a[i + 1] - a[i], q = a[i + 2] - a[i + 1];
        if (dengyu(len(p), 6.0) && dengyu(len(q), 9.0))
        {
            return cro(p, q) > 0;
        }
    } //逆时针给点
    for (int i = 0; i <= n; i++)
    {
        po p = a[i + 1] - a[i], q = a[i + 2] - a[i + 1];
        if (dengyu(len(q), 6.0) && dengyu(len(p), 9.0))
        {
            return cro(q, p) > 0;
        }
    } //顺时针给点
    return 0;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        if (!sol())
            printf("left\n");
        else
            printf("right\n");
    }
    return 0;
}

D Points Construction Problem(构造)

题意:

初始一个无限大的二维平面点都是白色,给出 n n n m m m ,把 n n n 个点涂成黑色,并且 m m m 对点满足条件,即这个黑色点周围的点都和他颜色不同。

m m m 其实就是周长。枚举一个长度 a , b a,b ab 2 ∗ a + 2 ∗ b = m 2*a+2*b=m 2a+2b=m,表示必须在这个矩阵内,然后先染对角线上的点 M i n ( a , b ) Min(a,b) Min(a,b) 个点,就保证了周长,然后用 b f s bfs bfs 去拓展染色点,不能拓展出边界。

AC代码:

int vis[105][105];
int arr[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
int main()
{
	int t;
	sd(t);
	while (t--)
	{
		int n, m;
		sdd(n, m);
		if (m & 1)
		{
			puts("No");
			continue;
		}
		m >>= 1;
		int flag = 0;
		rep(a, 1, m / 2)
		{
			int b = m - a;
			if (n >= b && n <= a * b)
			{
				flag = 1;
				puts("Yes");
				int cnt = 0;
				queue<int> q;
				rep(i, 1, a)
				{
					rep(j, 1, b)
						vis[i][j] = 0;
				}
				rep(i, 1, b)
				{
					int x = min(i, a), y = min(i, b);
					pdd(x, y);
					vis[x][y] = 1;
					rep(j, 0, 3)
					{
						int tx = x + arr[j][0], ty = y + arr[j][1];
						if (tx >= 1 && tx <= a && ty >= 1 && ty <= b && !vis[tx][ty])
							q.push(tx), q.push(ty);
					}
					cnt++;
				}
				while (cnt != n)
				{
					int x = q.front();
					q.pop();
					int y = q.front();
					q.pop();
					if (vis[x][y])
						continue;
					vis[x][y] = 1;
					pdd(x, y);
					;
					cnt++;
					rep(j, 0, 3)
					{
						int tx = x + arr[j][0], ty = y + arr[j][1];
						if (tx >= 1 && tx <= a && ty >= 1 && ty <= b && !vis[tx][ty])
							q.push(tx), q.push(ty);
					}
				}
				break;
			}
		}
		if (flag == 0)
			puts("No");
	}
	return 0;
}

E Two Matchings(dp)

题意:

给一个序列 a i ai ai ,要找到两种不同的整个序列的两两匹配,使得所有两两匹配的差的和最小,输出这个和。

n = 4 、 6 n=4、6 n=46 的时候,答案是固定的,就是 2 ∗ ( a n − a 1 ) 2*(a_n-a_1) 2ana1,那就把序列分为若干个 4 、 6 4、6 46 长度的就行了。 d p dp dp 转移状态。

AC代码:

const int N = 2e5 + 50;
int n, m;
ll a[N];
ll dp[N];
 
int main()
{
    int t;
    sd(t);
    while (t--)
    {
 
        sd(n);
        rep(i, 1, n)
            sld(a[i]);
        sort(a + 1, a + 1 + n);
        dp[1] = dp[2] = dp[3] = dp[5] = INF;
        dp[4] = 2 * (a[4] - a[1]);
        dp[6] = 2 * (a[6] - a[1]);
        rep(i, 7, n)
        {
            if (i & 1)
                dp[i] = inf;
            else
                dp[i] = min(dp[i - 4] + 2 * (a[i] - a[i - 3]), dp[i - 6] + 2 * (a[i] - a[i - 5]));
        }
        pd(dp[n]);
    }
    return 0;
}

F Fraction Construction Problem(ExGcd)

题意:

给定 a , b a,b a,b,求出满足 c d + e f = a b \frac{c}{d}+\frac{e}{f}=\frac{a}{b} dc+fe=ba的任意一组的 c , d , r , f c,d,r,f c,d,r,f

通分得: c f − d c d f = a b \frac{cf-dc}{df}=\frac{a}{b} dfcfdc=ba

  1. g = g c d ( a , b ) > 1 g=gcd(a,b)>1 g=gcd(a,b)>1 ,令 b ‘ = b g , a ‘ = a g b^`=\frac{b}{g},a^`=\frac{a}{g} b=gba=ga

c f − d c d f = a b = a ‘ b ‘ \frac{cf-dc}{df}=\frac{a}{b}=\frac{a^`}{b^`} dfcfdc=ba=ba

此时 b ‘ < b b^`b<b d f = b ‘ , c f − d e = a ‘ df=b^`,cf-de=a^` df=b,cfde=a

这时可以构造出 d = b ‘ , f = 1 , c = ( a ‘ + b ‘ ) , e = 1 d=b^`,f=1,c=(a^`+b^`),e=1 d=bf=1c=(a+b)e=1

  1. g = g c d ( a , b ) > 1 g=gcd(a,b)>1 g=gcd(a,b)>1,把 b b b 分解成两个互质的部分 d , f d,f d,f ,且 d , f d,f d,f 均不为 b b b, 否则就是 − 1 -1 1

寻找 2 2 2 s q r t ( b ) sqrt(b) sqrt(b) 的素数,找出第一个被 b b b 整除的素数i,然后将 f f f 赋值为 b i \frac{b}{i} ib

接下来要开始求出 c , e c,e c,e。已知 b = d f b=df b=df ,所以根据原方程可得 a = c × f − e × d a=c×f−e×d a=c×fe×d ,求解 c c c e e e,就能用扩展欧几里得算法来求解 c , e c,e c,e

在求出 c c c e e e 之后要判断一下正负,通过 c c c e e e 的正负性判断两个相异质因数分别对应谁。

AC代码:

void exgcd(ll a, ll b, ll &x, ll &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return;
    }
    exgcd(b, a % b, y, x);
    y -= a / b * x;
}
const int N = 2e6 + 10;
int pri[N], cnt;
bool st[N];
int Mipri[N];
void init(int n)
{
    st[0] = st[1] = true;
    Mipri[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (!st[i])
            pri[cnt++] = i, Mipri[i] = i;
        for (int j = 0; j < cnt && 1ll * i * pri[j] <= n; j++)
        {
            st[i * pri[j]] = true;
            Mipri[i * pri[j]] = pri[j];
            if (i % pri[j] == 0)
                break;
        }
    }
}
int main()
{
    init(N - 1);
    int t;
    sd(t);
    while (t--)
    {
        int a, b;
        sdd(a,b);
        int g = gcd(a, b);
        if (g > 1)
        {
            int e = 1, f = 1, c = a / g + b / g, d = b / g;
            printf("%d %d %d %d\n", c, d, e, f);
        }
        else
        {
            ll d = 1, f = b, k = Mipri[b];
            while (k != 1 && f % k == 0)
                d *= k, f /= k;
            if (d == b && f == 1)
                puts("-1 -1 -1 -1");
            else
            {
                ll e, c;
                exgcd(d, f, e, c);
                e = -e;
                while (e <= 0 || c <= 0)
                {
                    c += d;
                    e += f;
                }
                e *= a, c *= a;
                printf("%lld %lld %lld %lld\n", c, d, e, f);
            }
        }
    }
    return 0;
}

G Operating on a Graph

题意 :

给一个 n n n 条边 m m m 个点的无向图,一开始第 i i i 点就是第 i i i 组,然后依次给你 q q q 个分组使得与其相邻的分组变成 q i q_i qi 组,合并过程中肯定会消失分组,如果操作中第 q i q_i qi 个分组不存在就直接进行下一个操作。

学弟做的。

AC代码:

#include
using namespace std;
 
const int mxn=1600010,mxm=mxn<<1;
int n,m;
int hd[mxn],nx[mxm],v[mxm],e=0;
int f[mxn];
bool del[mxn];
int lhd[mxn],lnx[mxn],lta[mxn];
 
int find(int x){
    if(f[x]==x) return x;
    return f[x]=find(f[x]);
}
void ade(int x,int y){
    if(x==y) return;
    nx[++e]=hd[x];
    hd[x]=e;
    v[e]=y;
}
int st[mxn],t;
void merg(int x,int y){
    f[y]=x;
    int z=lhd[x];
    lhd[x]=lhd[y];
    lnx[lta[y]]=z;
    if(z==0) lta[x]=lta[y];
}
void sol(int o){
    if(find(o)!=o) return;
    t=0;
    for(int x=lhd[o];x;x=lnx[x]){
        st[++t]=x;
        del[x]=1;
    }
    lhd[o]=lta[o]=0;
    for(int p=1;p<=t;p++){
        int x=st[p];
        for(int i=hd[x];i;i=nx[i]){
            int y=v[i];
            if(del[y]) continue;
            int fy=find(y);
            if(fy==o) continue;
            merg(o,fy);
        }
    }
}
void solmain(){
    scanf("%d%d",&n,&m);
    e=0;
    for(int i=1;i<=n;i++){
        hd[i]=0;
        f[i]=i;
        del[i]=0;
        lhd[i]=lta[i]=i;
        lnx[i]=0;
    }
    while(m--){
        int x,y;
        scanf("%d%d",&x,&y);
        x++; y++;
        ade(x,y);
        ade(y,x);
    }
    int Q;
    scanf("%d",&Q);
    while(Q--){
        int o;
        scanf("%d",&o);
        o++;
        sol(o);
    }
    for(int i=1;i<=n;i++) printf("%d ",find(i)-1);
    printf("\n");
}
int main(){
//  freopen("g.in","r",stdin);
//  freopen("g.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        solmain();
    }
    return 0;
}

L Problem L is the Only Lovely Problem(签到)

题意:

判断一个串的前缀是不是 l o v e l y lovely lovely ,不看大小写。

AC代码:

const int N = 1e5 + 50;
int n, m;
int a[N];
 
int main()
{
    string s;
    cin >> s;
    if ((s[0] == 'L' || s[0] == 'l') && (s[1] == 'O' || s[1] == 'o') && (s[2] == 'V' || s[2] == 'v') && (s[3] == 'E' || s[3] == 'e') && (s[4] == 'L' || s[4] == 'l') && (s[5] == 'Y' || s[5] == 'y'))
        puts("lovely");
    else
        puts("ugly");
    return 0;
}

你可能感兴趣的:(牛客)