719 - A - Vitya in the Countryside
题目大意
一个月有
31
天,每天月亮的尺寸为
0,1,…,14,15,14,…,1,
,第二月又重复上面循环,现给出连续
n
天的月亮尺寸求判断下一天月亮尺寸的大小变化,不能确定则输出
思路 - 模拟
按照题意判断即可,注意坑点有最后一天是
0
和
代码
#include <cstdio>
using namespace std;
int n, pre, cur;
int main() {
while(1 == scanf("%d", &n)) {
scanf("%d", &cur);
for(int i = 1; i < n; ++i) {
pre = cur;
scanf("%d", &cur);
}
if(cur == 0) {
printf("UP\n");
}
else if(cur == 15) {
printf("DOWN\n");
}
else if(n == 1) {
printf("-1\n");
}
else {
printf("%s\n", pre < cur ? "UP" : "DOWN");
}
}
return 0;
}
719 - B - Anatoly and Cockroaches
题目大意
有一个
01
串,现要求
0
和
思路 - 贪心
按照题目要求,再枚举奇数位放
0
和
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
int getMin(int cnt_b[3], int cnt_r[3]) {
int num = n / 2 + (n & 1), tmp;//num为r字符的应有值
//令第一个字符为r字符
if(cnt_r[0] + cnt_r[1] <= num) {//若r的字符个数小于等于应有值,则需要将b字符转换成r字符
tmp = num - cnt_r[0] - cnt_r[1];//需要将tmp个b字符转换成r字符
//可以确定在0,2,4...的b字符一定大于等于tmp,否则不满足字符数等于应有值
cnt_b[0] -= tmp;//将所有需要转换的字符都在正确位置转换
tmp += cnt_b[0];//剩余的不在正确位置的b字符需要交换操作
}
else {//需要将r字符转换成b字符
tmp = cnt_r[0] + cnt_r[1] - num;//需要将tmp个r字符转换成b字符
if(cnt_r[1] >= tmp) {
cnt_r[1] -= tmp;
}
else {
cnt_r[1] -= tmp - cnt_r[0];
cnt_r[1] = 0;
}
tmp += cnt_b[0];//剩余的不在正确位置的b字符需要交换操作
}
return tmp;
}
int cnt_b[3][3], cnt_r[3][3], ans, tmp, num;
char s[100003];
int main() {
while(1 == scanf("%d", &n)) {
scanf("%s", s);
cnt_b[0][0] = cnt_b[0][1] = cnt_r[0][0] = cnt_r[0][1] = 0;
cnt_b[1][0] = cnt_b[1][1] = cnt_r[1][0] = cnt_r[1][1] = 0;
for(int i = 0; i < n; ++i) {//统计每个字符在奇数位和偶数位出现的次数
if(s[i] == 'b') {
++cnt_b[0][i & 1];
++cnt_b[1][i & 1];
}
else {
++cnt_r[0][i & 1];
++cnt_r[1][i & 1];
}
}
printf("%d\n", min(getMin(cnt_b[0], cnt_r[0]), getMin(cnt_r[1], cnt_b[1])));
}
return 0;
}
618 - A - Efim and Strange Grade
题目大意
给定一个小数,可以对小数部分最多进行 t 次四舍五入,求能得到的数的最大值?
思路 - 模拟
从小数点后第一个大于等于
题解用的是
DP
,
dp[i]
表示让第
i
位进位到第
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, t, pos, ed, carry;
char s[200003];
int main() {
while(2 == scanf("%d%d", &n, &t)) {
scanf("%s", s + 1);
s[0] = '0';
for(pos = 1; pos <= n; ++pos) {
if(s[pos] == '.') {
break;
}
}
int i;
for(i = 1; i <= n; ++i) {
if(i > pos && '5' <= s[i] && s[i] <= '9') {
if(i - 1 == pos) {
++s[i - 2];
s[i - 1] = '\0';
i -= 2;
}
else {
++s[i - 1];
if(i > pos) {
s[i] = '\0';
}
--i;
}
carry = (s[i] == '0' + 10) ? 1 : 0;
--t;
break;
}
}
if(i <= n) {
while(i > pos && t > 0 && '5' <= s[i]) {
if(i - 1 == pos) {
++s[i - 2];
s[i - 1] = '\0';
i -= 2;
}
else {
++s[i - 1];
if(i > pos) {
s[i] = '\0';
}
--i;
}
carry = (s[i] == '0' + 10) ? 1 : 0;
--t;
}
}
while(carry == 1) {
s[i] = '0';
if(i - 1 == pos) {
++s[i - 2];
s[i - 1] = '\0';
i -= 2;
}
else {
++s[i - 1];
if(i > pos) {
s[i] = '\0';
}
--i;
}
carry = (s[i] == '0' + 10) ? 1 : 0;
}
printf("%s\n", s[0] == '0' ? s + 1 : s);
}
return 0;
}
718 - C - Sasha and Array
题目大意
给定一个数列
a[1],a[2],…,a[n]
,有两个操作:
①
1 l r x
:对
a[l],a[l+1],…,a[r]
,分别加上
x
①
思路 - 线段树 && 矩阵快速幂
肯定能想到用矩阵快速幂求斐波那契数列,但是没有想到利用矩阵快速幂的方法,用线段树维护矩阵。
叶子结点维护
lazy
应该也存成矩阵,不能存成指数,否则会
TLE
。刚开始存成指数,各种优化,还闯到
51
组数据。。。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson (i << 1)
#define rson ((i << 1) | 1)
using namespace std;
const int MAXN = 100003;
const int MAX_MAT = 2;
const long long MOD = 1e9+7;
struct Matrix {
int m[MAX_MAT][MAX_MAT];
bool operator == (const Matrix& b) const {
for(int i = 0; i < MAX_MAT; ++i) {
for(int j = 0; j < MAX_MAT; ++j) {
if(m[i][j] != b.m[i][j]) {
return false;
}
}
}
return true;
}
Matrix operator + (const Matrix& b) const {
Matrix c;
for(int i = 0; i < MAX_MAT; ++i) {
for(int j = 0; j < MAX_MAT; ++j) {
c.m[i][j] = (m[i][j] + b.m[i][j]) % MOD;
}
}
return c;
}
Matrix operator - (const Matrix& b) const {
Matrix c;
for(int i = 0; i < MAX_MAT; ++i) {
for(int j = 0; j < MAX_MAT; ++j) {
c.m[i][j] = (m[i][j] - b.m[i][j] + MOD) % MOD;
}
}
return c;
}
Matrix operator * (const Matrix& b) const {
Matrix c;
for(int i = 0; i < MAX_MAT; ++i) {
for(int j = 0; j < MAX_MAT; ++j) {
long long res = 0;
for(int k = 0; k < MAX_MAT; ++k) {
res += ((long long) m[i][k]) * b.m[k][j];
}
c.m[i][j] = res % MOD;
}
}
return c;
}
Matrix operator ^ (long long n) const;
};
const Matrix FIB = {1, 1,
0, 0
};
const Matrix P = {0, 1,
1, 1,
};
const Matrix I = {1, 0,
0, 1,
};
//由于使用了结构体本身的常量,所以需要定义成外部成员函数
Matrix Matrix :: operator ^ (long long n) const {
Matrix m = *this, b = I;
while(n > 0) {
if((n & 1) == 1) {
b = b * m;
}
n = n >> 1;
m = m * m;
}
return b;
}
struct Node {
int l, r;
Matrix sum, lazy;//刚开始一直把lazy存成指数,导致花式TLE
}tr[MAXN << 2];
int n, m;
int ope, L, R, X;
inline void pushDown(int i) {
if(!(tr[i].lazy == I) && tr[i].l != tr[i].r) {//叶子结点不下放,否则会越界
tr[lson].sum = tr[lson].sum * tr[i].lazy;
tr[lson].lazy = tr[lson].lazy * tr[i].lazy;
tr[rson].sum = tr[rson].sum * tr[i].lazy;
tr[rson].lazy = tr[rson].lazy * tr[i].lazy;
tr[i].lazy = I;
}
}
inline void pushUp(int i) {
tr[i].sum = tr[lson].sum + tr[rson].sum;
}
void build(int i, int l, int r) {
tr[i].l = l;
tr[i].r = r;
tr[i].lazy = I;
if(l == r) {
scanf("%d", &X);
tr[i].sum = FIB;
tr[i].sum = tr[i].sum * (P ^ (X - 1));
return ;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushUp(i);
}
void modify(int i, const Matrix& temp) {
if(L <= tr[i].l && tr[i].r <= R) {
tr[i].sum = tr[i].sum * temp;
tr[i].lazy = tr[i].lazy * temp;
return ;
}
pushDown(i);
int mid = tr[lson].r;
if(mid >= L) {
modify(lson, temp);
}
if(mid < R) {
modify(rson, temp);
}
pushUp(i);
}
int query(int i) {
if(L <= tr[i].l && tr[i].r <= R) {
return tr[i].sum.m[0][0];
}
pushDown(i);
int mid = tr[lson].r;
int res = 0;
if(mid >= L) {
res = query(lson);
}
if(mid < R) {
res += query(rson);
}
pushUp(i);
return res % MOD;
}
int main() {
while(2 == scanf("%d%d", &n, &m)) {
build(1, 1, n);
while(m-- >0) {
scanf("%d%d%d", &ope, &L, &R);
if(ope == 1) {
scanf("%d", &X);
modify(1, P ^ X);
}
else {
printf("%d\n", query(1));
}
}
}
return 0;
}
718 - D - Andrew and Chemistry
题目大意
给定一颗无根树,现可以添加一个结点,使得添加后形成不同构的树有多少?保证添加前后每个结点的度不能超过 4 。
思路 - 树形DP
题解的方法好巧妙,由于每个结点
可以想到
可以不枚举,再从上往下更新状态值就能得到所有点作为根时的状态值。大致是递归前,令当前结点
i
的以前的子结点
代码
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#define lson (i << 1)
#define rson ((i << 1) | 1)
using namespace std;
const int MAXN = 100003;
const int INF = 0x3f3f3f3f;
struct Vect {
int a[4];
Vect() {
memset(a, 0x3f, sizeof(a));
}
bool operator < (const Vect& x) const {
for(int i = 0; i < 4; ++i) {
if(a[i] != x.a[i]) {
return a[i] < x.a[i];
}
}
return false;
}
};
struct Node {
int v, nxt;
}edge[MAXN << 1];
int fir[MAXN], tot;
void init() {
memset(fir, -1, sizeof(fir));
tot = 0;
}
void addEdge(int u, int v) {
edge[tot].v = v;
edge[tot].nxt = fir[u];
fir[u] = tot++;
edge[tot].v = u;
edge[tot].nxt = fir[v];
fir[v] = tot++;
}
int n, ans, cnt;
//a[i]表示只看子结点时的状态值,b[i]表示看所有结点时的状态值
int a[MAXN], b[MAXN], deg[MAXN];//deg[i]表示i点的度
bool vis[MAXN * 3];//【注意】由于dfsFromDown中cnt至多+n次,dfsFromUp中cnt至多+(n+n)次,所以要开三倍空间
map<Vect, int> mp;
void dfsFromDown(int u, int p) {//自底向上更新a
Vect cur;
int num = 0, v;
for(int i = fir[u]; i != -1; i = edge[i].nxt) {
v = edge[i].v;
if(v != p) {
dfsFromDown(v, u);
cur.a[num++] = a[v];
}
}
sort(cur.a, cur.a + 4);//按子结点状态的升序排序
if(mp[cur] == 0) {//如果当前状态未出现过,则标记为一个新的状态
mp[cur] = ++cnt;
}
a[u] = mp[cur];
}
//status表示p结点以u结点为父亲结点时应有的状态值a[p]
void dfsFromUp(int u, int p, int status) {//自顶向下更新b
Vect cur;
int num = 0, v;
for(int i = fir[u]; i != -1; i = edge[i].nxt) {
v = edge[i].v;
if(v == p) {
cur.a[num++] = status;
}
else {
cur.a[num++] = a[v];
}
}
sort(cur.a, cur.a + 4);//按子结点状态的升序排序
if(mp[cur] == 0) {//如果当前状态未出现过,则标记为一个新的状态
mp[cur] = ++cnt;
}
b[u] = mp[cur];
for(int i = fir[u]; i != -1; i = edge[i].nxt) {
v = edge[i].v;
if(v != p) {
for(int i = 0; i < 4; ++i) {
if(cur.a[i] == a[v]) {
cur.a[i] = INF;
break;
}
}
sort(cur.a, cur.a + 4);//按子结点状态的升序排序
if(mp[cur] == 0) {//如果当前状态未出现过,则标记为一个新的状态
mp[cur] = ++cnt;
}
dfsFromUp(v, u, mp[cur]);
cur.a[3] = a[v];
}
}
}
int u, v;
int main() {
while(1 == scanf("%d", &n)) {
init();
mp.clear();
ans = cnt = 0;
memset(deg, 0, sizeof(deg));
memset(vis, false, sizeof(vis));
for(int i = 1; i < n; ++i) {
scanf("%d%d", &u, &v);
addEdge(u, v);
++deg[u];
++deg[v];
}
dfsFromDown(1, 0);
dfsFromUp(1, 0, 0);
for(int i = 1; i <= n; ++i) {
if(deg[i] < 4 && !vis[b[i]]) {//当前结点的度小于4且状态未出现过,则可以添一个结点形成新的不同的树
vis[b[i]] = true;
++ans;
}
}
printf("%d\n", ans);
}
return 0;
}
718 - E -
题目大意
思路 -
代码