【AtCoder】AGC026 题解

A - Colorful Slimes 2

找相同颜色的一段,然后答案加上段长除2下取整

代码

#include <iostream>
#include <cstdio>
using namespace std;
int N;
int a[105];
int main() {
    scanf("%d",&N);
    for(int i = 1 ; i <= N ; ++i) {
        scanf("%d",&a[i]);
    }
    int ans = 0;
    int cnt = 0;
    for(int i = 1 ; i <= N ; ++i) {
        if(a[i] != a[i - 1]) {
            ans += cnt / 2;
            cnt = 0;
        }
        ++cnt;
    }
    ans += cnt / 2;
    printf("%d\n",ans);
}

B - rng_10s

题解

如果A < B一定不可以
如果C>=B并且D>=B显然一定可以
如果D < B那么一定不可以
然后如果A大于C,那么就买几个B使得A减到C以下,如果减的过程中买不到了,那么就不可以

每次买完还需要补货的时候相当于从A开始,每次加上B和D的gcd,如果这个值落在了C和B之间,那么就不合法了

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
//#define ivorysi
#define fi first
#define se second
#define MAXN 25005
#define enter putchar('\n')
#define space putchar(' ')
typedef long long int64;
using namespace std;
template <class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
        c = getchar();
        if(c == '-') f = -1;
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = getchar();
    }
    res *= f;
}
template <class T>
void out(T x) {
    if(x < 0) {x = -x;}
    if(x >= 10) {
        out(x / 10);
    }
    putchar('0' + x % 10);
}
int T;
int64 A,B,C,D;
int64 gcd(int64 a,int64 b) {
    return b == 0 ? a : gcd(b,a % b);
}
void Solve() {
    read(A);read(B);read(C);read(D);
    if(A < B) {puts("No");}
    else {
        if(C >= B && D >= B) puts("Yes");
        else if(D < B) puts("No");
        else {
            if(A > C) {
                int64 t = (A - C - 1) / B + 1;
                if(t > A / B) {
                    puts("No");return;
                }
                A -= B * t;
            }
            int64 g = gcd(D % B,B);
            if((C + 1 - A - 1) / g + 1 == (B - A - 1) / g + 1) puts("Yes");
            else puts("No");
        }
    }
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    read(T);
    while(T--) {
        Solve();
    }
}

C - String Coloring

题解

直接折半搜索即可
用哈希表维护前半部分拆分方式的两个字符串的哈希值
再枚举后一半的拆分方式查哈希表即可

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define enter putchar('\n')
#define space putchar(' ')
#define fi first
#define se second
#define ba 47
#define mo 999999137
#define mod 974711
//#define ivorysi
#define pii pair<int,int>
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
	if(c == '-') f = -1;
	c = getchar();
    }
    while(c >= '0' && c <= '9') {
	res = res * 10 + c - '0';
	c = getchar();
    }
    res *= f;
}
 
template<class T>
void out(T x) {
    if(x < 0) {putchar('-');x = -x;}
    if(x >= 10) out(x / 10);
    putchar('0' + x % 10);
}
struct Hash {
    char t[20];
    int64 val;
    int len;
    friend bool operator < (const Hash &a,const Hash &b) {
	return a.val < b.val;
    }
    friend bool operator == (const Hash &a,const Hash &b) {
	if(a.val != b.val) return false;
	if(a.len != b.len) return false;
	for(int i = 1 ; i <= a.len ; ++i) {
	    if(a.t[i] != b.t[i]) return false;
	}
	return true;
    }
}B[1000005];
int cnt;
int64 e[55];
struct Mmp {
    struct node {
	int next,v;
	int64 x;
    }E[1000005];
    int head[mod + 5],sumE;
    void clear() {
	memset(head,0,sizeof(head));
	sumE = 0;
    }
    void add(int64 x,int v) {
	int u = x % mod;
	E[++sumE].x = x;
	E[sumE].v = v;
	E[sumE].next = head[u];
	head[u] = sumE;
    }
    void Insert(int64 x) {
	int u = x % mod;
	for(int i = head[u] ; i ; i = E[i].next) {
	    if(E[i].x == x) {E[i].v++;return;}
	}
	add(x,1);
    }
    int Query(int64 x) {
	int u = x % mod;
	for(int i = head[u]; i ; i = E[i].next) {
	    if(E[i].x == x) return E[i].v;
	}
	return 0;
    }
}M1,M2;
int64 Calc(char *s,int l) {
    int64 res = 0;
    for(int i = 1 ; i <= l ; ++i) {
	res += (s[i] - 'a' + 1) * e[i] % mo;
	res %= mo;
    }
    return res;
}
void Insert(char *s,int l) {
    ++cnt;
    for(int i = 1 ; i <= l ; ++i) B[cnt].t[i] = s[i]; 
    B[cnt].len = l;B[cnt].val = Calc(s,l);
}
int N;
char s[55];
void Solve() {
    read(N);
    e[0] = 1;
    for(int i = 1 ; i <= N ; ++i) e[i] = e[i - 1] * ba % mo;
    scanf("%s",s + 1);
    char t1[25],t2[25];
    int c1,c2;
    for(int S = 0 ; S < (1 << N) ; ++S) {
	c1 = c2 = 0;
	int T = 0;
	for(int i = 1 ; i <= N ; ++i) {
	    if(S >> (i - 1) & 1) t1[++c1] = s[i];
	    else {t2[++c2] = s[i];T |= (1 << i - 1);}
	}
	if(S > T) continue;
	if(c1) Insert(t1,c1);
	if(c2) Insert(t2,c2);
    }
    sort(B + 1,B + cnt + 1);
    cnt = unique(B + 1,B + cnt + 1) - B - 1;
    M1.clear();M2.clear();
    for(int i = 1 ; i <= cnt ; ++i) {
	M1.add(B[i].val,i);
    }
    for(int S = 0 ; S < (1 << N) ; ++S) {
	c1 = c2 = 0;
	for(int i = 1 ; i <= N ; ++i) {
	    if(S >> (i - 1) & 1)  t1[++c1] = s[i];
	    else {t2[++c2] = s[i];}
	}
	int d1 = 0,d2 = 0;
	if(c1) d1 = M1.Query(Calc(t1,c1));
	if(c2) d2 = M1.Query(Calc(t2,c2));
	M2.Insert(1LL * d1 * (cnt + 1) + d2);
    }
    int64 ans = 0;
    for(int S = 0 ; S < (1 << N) ; ++S) {
	c1 = c2 = 0;
	for(int i = N + 1 ; i <= 2 * N ; ++i) {
	    if(S >> (i - N - 1) & 1) t1[++c1] = s[i];
	    else {t2[++c2] = s[i];}
	}
	reverse(t1 + 1,t1 + c1 + 1);
	reverse(t2 + 1,t2 + c2 + 1);
	int d1 = 0,d2 = 0;
	if(c1) d1 = M1.Query(Calc(t1,c1));
	if(c2) d2 = M1.Query(Calc(t2,c2));
	ans += M2.Query(1LL * d1 * (cnt + 1) + d2);
    }
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
}

D - Histogram Coloring

题解

我们考虑从上往下,对于每个分离的直方图的横列进行dp,然后再把他们合起来

我们发现,如果直方图不是10101010或0101010101的形式,那么以下所有列就只有一种放置方法

那么我们记录两个值,一个是直方图最下面一行填的是1010101或01010101,记为dp[S][1],一个是所有的方案数记为dp[S][2]

转移方法是对于一个列数一样的矩形块
dp[S][1]是它上面所有的直方图dp[S][1]乘起来,然后再乘上2的行数次幂
dp[S][2]我们认为是如果是010101或101010,那么下面对应的有两种方式,剩下的只有一种,其余的方块随便填,下面每一行是上一行取反
是上面所有直方图的(dp[T][1] + dp[T][2])乘起来,然后再乘上2的剩余方块数的幂
但是dp[S][2]也应该包括相邻两行有相同的情况,我们就再加上dp[S][1],同时减掉dp[S][1]中我们每行都不同的情况

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define enter putchar('\n')
#define space putchar(' ')
#define fi first
#define se second
#define mp make_pair
#define ba 47
#define mo 999999137
#define mod 974711
//#define ivorysi
#define pii pair<int,int>
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
	if(c == '-') f = -1;
	c = getchar();
    }
    while(c >= '0' && c <= '9') {
	res = res * 10 + c - '0';
	c = getchar();
    }
    res *= f;
}
 
template<class T>
void out(T x) {
    if(x < 0) {putchar('-');x = -x;}
    if(x >= 10) out(x / 10);
    putchar('0' + x % 10);
}
const int MOD = 1000000007;
int N;
int h[105];
bool vis[105];
int inc(int a,int b) {
    return a + b >= MOD ? a + b - MOD : a + b;
}
int mul(int a,int b) {
    return 1LL * a * b % MOD;
}
int fpow(int x,int c) {
    int res = 1,t = x;
    while(c) {
	if(c & 1) res = mul(res,t);
	t = mul(t,t);
	c >>= 1;
    }
    return res;
}
void update(int &x,int y) {
    x = inc(x,y);
}
pii Solve(int l,int r,int v) {
    int minv = 1e9;
    for(int i = l ; i <= r ; ++i) minv = min(minv,h[i]);
    int p,cnt = 0;
    pii res = mp(1,1);
    for(int i = l ; i <= r ; ++i) {
	if(h[i] > minv) ++cnt;  
    }
    for(int i = l ; i <= r ; ++i) {
	if(h[i] > minv) {
	    p = i;
	    while(p < r && h[p + 1] > minv) ++p;
	    pii f = Solve(i,p,minv);
	    res.fi = mul(res.fi,f.fi);
	    res.se = mul(res.se,inc(f.se,f.fi));
	    i = p;
	}
    }
    int t = mul(res.fi,2);
    res.se = mul(res.se,fpow(2,r - l + 1 - cnt));
    res.fi = mul(res.fi,fpow(2,minv - v));
    res.se = inc(res.se,inc(res.fi,MOD - t));
    return res; 
}
void Solve() {
    read(N);
    for(int i = 1 ; i <= N ; ++i) {read(h[i]);}
    pii ans = Solve(1,N,0);
    out(ans.se);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
}

E - Synchronized Subsequence

题解

如果b在a前就一直选,选到某个最大的a的位置会更改成一段a在b前
如果这个a在b前是最后一部分就取到所有的abababab...前,否则就跳过这一段
具体就用string维护一下后缀能取到的max字符串就好了

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <ctime>
#include <map>
#include <set>
#define fi first
#define se second
#define pii pair<int,int>
//#define ivorysi
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define MAXN 6005
using namespace std;
typedef long long int64;
typedef double db;
typedef unsigned int u32;
template<class T>
void read(T &res) {
	res = 0;T f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9' ) {
		res = res * 10 - '0' + c;
		c = getchar();
	}
	res *= f;
}
template<class T>
void out(T x) {
	if(x < 0) {x = -x;putchar('-');}
	if(x >= 10) {
		out(x / 10);
	}
	putchar('0' + x % 10);
}
namespace task {
	char s[MAXN];
	int N,a[MAXN],b[MAXN],tota,totb,pos[MAXN];
	string str[MAXN];
	bool vis[MAXN];
	void Init() {
		read(N);
		scanf("%s",s + 1);
		tota = 0;totb = 0;
		for(int i = 1 ; i <= 2 * N ; ++i) {
			if(s[i] == 'a') {a[++tota] = i;pos[i] = tota;}
			else {b[++totb] = i;pos[i] = totb;}
		}
	}
	void Solve() {
		str[2 * N + 1] = "";
		for(int i = 2 * N ; i >= 1 ; --i) {
			str[i] = max(str[i],str[i + 1]);
			string t = "";
			int p = pos[i];
			if(s[i] == 'a' && a[p] < b[p]) {
				str[i] = max(str[i],"ab" + str[b[p] + 1]);
			}
			else if(s[i] == 'b' && b[p] < a[p]) {
				memset(vis,0,sizeof(vis));
				int maxv = a[p];
				for(int j = i ; j <= maxv ; ++j) {
					if(s[j] == 'b') {
						p = pos[j];
						vis[b[p]] = 1;vis[a[p]] = 1;
						maxv = max(a[p],maxv);
					}
					if(vis[j]) t += s[j];
				}
				t += str[maxv + 1];
				str[i] = max(str[i],t);
			}
		}
		cout<<str[1]<<endl;
	}
}
int main() {
#ifdef ivorysi
	freopen("f1.in","r",stdin);
#endif
	task::Init();
	task::Solve();
	return 0;
}

F - Manju Game

题解

我们把整个序列黑白染色变成
BWBWBWBW的形式

如果是偶数的话
先手可以保证自己得到全部的B或全部的W
后手可以在先手做出选择后,保证自己得到另一种颜色的所有
这样的话,相当于先手得到黑白颜色中较多的那个,后手得到较少的那个

如果是奇数的话
先手还是可以保证自己得到所有B
那么先手选W的话解会不会更优
先手在选B之后,便不可能再选W,如果某一次B进行完了以后先手选了W结果更优,后手可以撤销上一次操作,使得先后手所拿到的物品相反,结果更劣

先手假如可以选\(W_1,W_2,W_3...W_k\)如果,我们能取的区间如果全取B的话,这个区间的B - W是最小的,所以我们限定一下这个区间B - W的最小值,二分一下,然后用一个dp判断即可

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define enter putchar('\n')
#define space putchar(' ')
#define fi first
#define se second
#define mp make_pair
//#define ivorysi
#define pii pair<int,int>
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < '0' || c > '9') {
	if(c == '-') f = -1;
	c = getchar();
    }
    while(c >= '0' && c <= '9') {
	res = res * 10 + c - '0';
	c = getchar();
    }
    res *= f;
}
 
template<class T>
void out(T x) {
    if(x < 0) {putchar('-');x = -x;}
    if(x >= 10) out(x / 10);
    putchar('0' + x % 10);
}
int N;
int64 a[300005],sum[300005][2];
bool check(int64 mid) {
    int64 t = 0; 
    for(int i = 2 ; i <= N ; i += 2) {
	if(sum[i - 1][1] - sum[i - 1][0] + t >= mid) {
	    t = max(sum[i][0] - sum[i][1],t);
	}
    }
    if(sum[N][1] - sum[N][0] + t >= mid) return true;
    return false;
}
void Solve() {
    read(N);
    for(int i = 1 ; i <= N ; ++i) read(a[i]);
    int64 L = 0,R = 0;
    for(int i = 1 ; i <= N ; ++i) {
	R += a[i];L -= a[i];
	sum[i][0] = sum[i - 1][0];
	sum[i][1] = sum[i - 1][1];
	sum[i][i & 1] += a[i];
    }
    if(N % 2 == 0) {
	out(max(sum[N][0],sum[N][1]));space;out(min(sum[N][0],sum[N][1]));enter;
	return;
    }
    while(L < R) {
	int64 mid = (L + R + 1) >> 1;
	if(check(mid)) L = mid;
	else R = mid - 1;
    }
    int64 a = L + sum[N][0];
    int64 b = sum[N][0] + sum[N][1] - a;
    out(a);space;out(b);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    Solve();
}
posted @ 2018-10-11 21:51  sigongzi  阅读(275)  评论(0编辑  收藏  举报