Codeforces 461 A. Appleman and Toastman:简单的贪心.
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define cntbit __builtin_popcount
using namespace std;
//#pragma comment(linker,"/STACK:102400000,102400000")
typedef long long LL;
const int maxn = 300000 + 333;
LL a[maxn];
LL sum[maxn];
int MAIN()
{
int n;
cin>>n;
for (int i = 1; i <= n; i ++) {
cin>>a[i];
}
if (n == 1) {
cout<<a[1]<<endl;
return 0;
}
sort(a, a + n + 1);
sum[n] = a[n];
LL ans = 0;
for (int i = n - 1; i >= 1; i --) {
sum[i] = sum[i + 1] + a[i];
ans += sum[i];
}
ans += sum[1];
cout<<ans<<endl;
return 0;
}
int main()
{
ios :: sync_with_stdio(false);
return MAIN();
}
Codeforces 461 B. Appleman and Tree
题意:给定一棵包含n个节点的树,这棵树的一些节点(至少一个)被染成黑色,现在可以删除一些边,使得删完边后的每个联通分量中恰好只有一个黑色节点.问满足条件的方案数一共有多少(对1e9+7取mod).
解法:树形dp.
dp[u][0]:以u为根的子树没有一个黑色节点的方法数;
dp[u][1]:以u为根的子树恰好只有一个黑色节点的方法数.
如果点u的颜色是黑色:则dp[u][1]为所有孩子(dp[v][0]+dp[v][1])的积.(因为对于一个孩子节点v,如果以v为根的子树此时是不包含黑色节点的,那么此时必定与u连边,如果是包含一个黑色节点的,则必定与u不连边.也就是说,对于每一个儿子节点的每个状态,都有了一个决策去连或者不连边.)
如果点u的颜色是白色:则dp[u][0]为所有孩子(dp[v][0]+dp[v][1])的积(原因类似).
而对于dp[u][1]的计算有两种计算方法.
方法一:由于dp[u][0]已经计算,那么对于一个孩子v,u选择与v为根的包含一个黑色节点的连边的方法数就是dp[u][0]/(dp[v][0]+dp[v][1])*dp[v][1].这一步需要求乘法逆元,最终程序的复杂度为nlogn.
方法二:可以类似dp的思想同时计算dp[u][0]与dp[u][1],初始状态下dp[u][0]=1,dp[u][1]=0.对于每一个孩子v,dp的转移方程如下:dp[u][0]=dp[u][0]*(dp[v][0]+dp[v][1]),dp[u][1]=dp[u][0]*dp[v][1]+dp[u][1]*(dp[v][0]+dp[v][1]).这样的复杂度是O(n).
这样的两种dp方法实际上也是在树形dp中经常出现的两种方法.
/*
注意点:
1.树形dp的时候,如果是写成dfs(u,fa)的形式,不需要vis数组
2.分清楚子树和根之间的关系,注意好到底是乘法原理还是加法原理
比如说这道题,子树的状态只是决定了根和子树之间的边的状态,所以对于每棵子树的每一个状态,我都可以选择是否删除对应的边来达到某个状态
*/
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <algorithm>
#include <string>
#include <map>
#include <iostream>
#include <iomanip>
#include <list>
#include <queue>
#define REP(i, n) for ((i) = 0; (i) < (int)(n); ++ (i))
using namespace std;
typedef long long LL;
const int maxn = 100000 + 100;
const LL MOD = 1000000000 + 7;
LL dp[maxn][2];
int c[maxn];
vector<int> g[maxn];
void dfs(int u, int fa)
{
if (c[u] == 1) {
dp[u][1] = 1;
for (int i = 0; i < g[u].size(); i ++) {
int v = g[u][i];
if (v != fa) {
dfs(v, u);
dp[u][1] = (dp[u][1] * ((dp[v][0] + dp[v][1]) % MOD)) % MOD;
}
}
}
else
{
dp[u][0] = 1;
for (int i = 0; i < g[u].size(); i ++) {
int v = g[u][i];
if (v != fa) {
dfs(v, u);
LL v1, v2;
v1 = dp[u][0] * ((dp[v][0] + dp[v][1]) % MOD) % MOD;
v2 = (dp[u][0] * dp[v][1] % MOD) + dp[u][1] * ((dp[v][0] + dp[v][1]) % MOD) % MOD;
v2 = v2 % MOD;
dp[u][0] = v1;
dp[u][1] = v2;
}
}
}
}
/*
void extend_gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(!b)
{
d=a;
x=1;
y=0;
}else{
extend_gcd(b,a%b,d,y,x);
y-=x*(a/b);
}
}
LL inv(LL a,LL n)
{
LL d,x,y;
extend_gcd(a, n, d, x, y);
return d == 1? (x+n)%n:-1;
}
void dfs(int u, int fa)
{
int size = (int)g[u].size();
int i;
REP(i, size)
{
int v = g[u][i];
if (v == fa) continue;
dfs(v, u);
}
if (c[u] == 1) {
dp[u][1] = 1;
REP(i, size)
{
int v = g[u][i];
if (v == fa) continue;
dp[u][1] = dp[u][1] * ((dp[v][0] + dp[v][1]) % MOD) % MOD;
}
}
else
{
dp[u][0] = 1;
REP(i, size)
{
int v = g[u][i];
if (v == fa) continue;
dp[u][0] = dp[u][0] * ((dp[v][0] + dp[v][1]) % MOD) % MOD;
}
REP(i, size)
{
int v = g[u][i];
if (v == fa) continue;
LL val = dp[u][0] * inv(dp[v][0] + dp[v][1], MOD) % MOD;
dp[u][1] += val * dp[v][1] % MOD;
}
dp[u][1] %= MOD;
}
}
*/
int MAIN()
{
int n;
cin>>n;
int p;
for (int i = 0; i + 1 < n; i ++) {
cin>>p;
g[p].push_back(i + 1);
g[i + 1].push_back(p);
}
for (int i = 0; i < n; i ++) {
cin>>c[i];
}
dfs(0, -1);
LL ans = dp[0][1];
ans = (ans + MOD) % MOD;
cout<<ans<<endl;
return 0;
}
int main()
{
ios :: sync_with_stdio(false);
cout << fixed << setprecision(16);
return MAIN();
}
Codeforces 461 C. Appleman and a Sheet of Paper
题意:给定一张长度为n的纸,每次会选择一个点作为中心对折.动态询问当前折纸从l到r这一段的总厚度(初始状态下厚度为1).
解法:树状数组,implementation,也需要正确地估计时间复杂度.
对于任意一个状态下的折纸,我们用l,r和方向sgn来描述(比如从左向右折纸的但是右边那块长度很短的话可以看作是从右向左折纸),而对于每一步的折纸操作,直接把原来地方的厚度加到了现在的位置.利用树状数组可以在求和和加上的复杂度为logn.而总的折纸并做加的操作并不会大于n.所以总的复杂度是O(n*logn+q*logn).
/*
树状数组的应用
对复杂度的正确估计
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
using namespace std;
const int MaxN = 100000;
int n;
int bit[MaxN + 1];
inline void bit_add(int p, int x)
{
for (int i = p; i <= n; i += i & -i)
bit[i] += x;
}
inline int bit_query(int p)
{
int res = 0;
for (int i = p; i >= 1; i -= i & -i)
res += bit[i];
return res;
}
int main()
{
int nQ;
static int a[MaxN + 1];
cin >> n >> nQ;
for (int i = 1; i <= n; i++)
a[i] = 1, bit_add(i, 1);
int le = 1, ri = n, d = 1;
while (nQ--)
{
int type;
scanf("%d", &type);
if (type == 1)
{
int len;
scanf("%d", &len);
if (len * 2 <= abs(le - ri) + 1)
{
for (int i = 0; i < len; i++)
{
bit_add(le + (len + i) * d, a[le + (len - i - 1) * d]);
a[le + (len + i) * d] += a[le + (len - i - 1) * d];
}
le += len * d;
}
else
{
len = abs(le - ri) + 1 - len;
for (int i = 0; i < len; i++)
{
bit_add(ri - (len + i) * d, a[ri - (len - i - 1) * d]);
a[ri - (len + i) * d] += a[ri - (len - i - 1) * d];
}
ri -= len * d;
d *= -1;
swap(le, ri);
}
}
else if (type == 2)
{
int l, r;
scanf("%d %d", &l, &r);
r--;
l = le + l * d;
r = le + r * d;
if (l > r)
swap(l, r);
int res = bit_query(r) - bit_query(l - 1);
printf("%d\n", res);
}
}
return 0;
}
Codeforces 461 D. Appleman and Complicated Task
题意:给定一个n*n的表,每个格子可以填上o或者x,需要每个格子周围的o的个数都是偶数.给定k个格子已经填的数,问满足题目要求的放置方法一共有多少种.
解法:假设o为1,x为0.我们发现,第一行的状态就决定了所有格子的状态.再利用异或关系,题目等价为a[i]^a[i+1]^...^a[j]=k,给定若干组(i,j,k),问长度为n的可能有多少种.
这个问题可以利用并查集解决,每个a[i]拆为两个点2*i,2*i+1.
若k为1,则将2*i与2*(j+1)+1合并,2*i+1与2*(j+1)合并.
若k为0,则将2*i与2*(j+1)合并,2*i+1与2*(j+1)+1合并.
最后答案为2^(集合数/2),若2*i与2*i+1在一个集合,答案为0.
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define cntbit __builtin_popcount
using namespace std;
//#pragma comment(linker,"/STACK:102400000,102400000")
typedef long long LL;
const int maxn = 100000 + 100;
const int mod = 1000000000 + 7;
int fa[maxn<<1];
int cnt;
int find(int x)
{
if (fa[x] == x) {
return x;
}
else
{
return fa[x] = find(fa[x]);
}
}
void merge(int a, int b)
{
a = find(a);
b = find(b);
if (a != b) {
cnt --;
fa[b] = a;
}
}
int abs(int x)
{
return x < 0 ? -x : x;
}
int MAIN()
{
int n, k;
cin>>n>>k;
cnt = 2 * n + 4;
for (int i = 0; i < cnt; i ++) {
fa[i] = i;
}
int a, b;
char op[2];
while (k --) {
cin>>a>>b>>op;
a--, b--;
int l, r;
l = abs(a - b);
r = min(a + b, 2 * n - 2 - a - b);
if (op[0] == 'o') {
merge(2 * l, 2 * (r + 2) + 1);
merge(2 * l + 1, 2 * (r + 2));
}
else
{
merge(2 * l, 2 * (r + 2));
merge(2 * l + 1, 2 * (r + 2) + 1);
}
}
for (int i = 0; i < n + 2; i ++) {
if (find(2 * i) == find(2 * i + 1)) {
cout<<"0"<<endl;
return 0;
}
}
cnt = (cnt - 4) / 2;
int ans = 1;
for (int i = 0; i < cnt; i ++) {
ans = (ans + ans) % mod;
}
cout<<ans<<endl;
return 0;
}
int main()
{
ios :: sync_with_stdio(false);
return MAIN();
}
Codeforces 461 E. Appleman and a Game
题意:给定两个串,s,t.如果从空串开始,每次只能在已有的串之后添加t的子串,可以得到最少的操作次数.现在给定s的长度,你需要确定构造出一个满足条件的s后,最多需要操作的次数.串t保证只出现A,B,C,D四个字母,并且每个字母至少出现1次.
解法:字符串,dp,矩阵.
假设给定了一个字符串s,我们的策略肯定是每次写尽可能长的子串,相反,要想s写的次数尽可能多,我们希望每次填写的子串尽可能短.
dp[k][c1][c2]:由k个字符串拼接而成,头字符是c1,末尾之后字符是c2的字符串的最短长度.
dp[k+1][c1][c2]=min(dp[k+1][c1][c2],dp[k][c1][c3]+dp[1][c3][c2]),这一步dp过程可以利用矩阵来加速.
接下来需要计算dp[1][c1][c2].我们枚举长度为L的字符串,如果该字符串不在t中出现过,则更新答案dp[1][c1][c2]=min(dp[1][c1][c2],L-1).注意到t的长度不超过10^5,所以我们枚举的长度应该不超过log4(T)+1.
之后二分答案,注意二分的写法.
#include <cstdio> #include <cstring> #include <set> #include <vector> #include <algorithm> #include <string> #include <map> #include <iostream> #include <iomanip> #include <list> #include <queue> #define REP(i, n) for ((i) = 0; (i) < (int)(n); ++ (i)) using namespace std; typedef long long ll; const ll INF = 1ll << 60; const int MAXN = 100000 + 100; int g[4][4], n; bool f[1ll << 20]; char t[MAXN]; int r[MAXN]; ll four[10]; void check(int l) { int i, j; REP(i, four[l]) f[i] = false; for (i = 0; i + l - 1 < n; ++ i) { int tmp = 0; REP(j, l) { tmp = tmp * 4 + r[i + j]; } f[tmp] = true; } REP(i, four[l]) { if (!f[i]) { int s = i / four[l - 1]; int e = i % 4; g[s][e] = min(g[s][e], l - 1); } } } struct Matrix { ll a[4][4]; }; Matrix pro(Matrix A, Matrix B) { int i, j, k; Matrix C; REP(i, 4) REP(j, 4) C.a[i][j] = INF; REP(i, 4) { REP(j, 4) { REP(k, 4) { C.a[i][k] = min(C.a[i][k], A.a[i][j] + B.a[j][k]); } } } return C; } ll cal(ll k) { Matrix A, B; int i, j; REP(i, 4) REP(j, 4) A.a[i][j] = g[i][j]; REP(i, 4) REP(j, 4) B.a[i][j] = 0; for (i = 0; i < 60; ++ i) { if ((k >> i) & 1) { B = pro(B, A); } A = pro(A, A); } ll ans = INF; REP(i, 4) REP(j, 4) ans = min(ans, B.a[i][j]); return ans; } int MAIN() { ll x; scanf("%lld", &x); scanf("%s", t); n = (int)strlen(t); int i, j; REP(i, n) r[i] = t[i] - 'A'; four[0] = 1; for (i = 1; i < 10; ++ i) four[i] = 4 * four[i - 1]; REP(i, 4) REP(j, 4) g[i][j] = 10; for (i = 2; i <= 10; ++ i) check(i); //REP(i, 4) REP(j, 4) cout << i << " " << j << " " << g[i][j] << endl; ll left = 0, right = INF; ll ans = 1; while (left <= right) { ll mid = (left + right) >> 1; ll val = cal(mid); if (val < x) { ans = mid; left = mid + 1; } else { right = mid - 1; } } printf("%lld\n", ans + 1); return 0; } int main() { cout << fixed << setprecision(16); //ios :: sync_with_stdio(false); return MAIN(); }