NAIPC 2016 【5/11】
B - Alternative Bracket Notation
給出了一種表示括號匹配的方法,要求對於給定的括號序列求出長度最小的表達。
通過觀察這種方法可以發現,它的基本實現過程仍然是不斷地把左括號扔進棧裡,然後每次遇到右括號的時候取出來。一對括號在表示式中佔的長度分為三部分,左右端點的數字長度以及2個字元的特殊字元。
用la[i]和lb[i]表示第i個位置上的括號,它左端點和右端點的數字長度。初始的時候顯然都為1,然後在將左括號扔進去的時候,左端點的值應該是當前的序列長度+兩個數字的長度+2,並更新當前序列長度。
這樣處理完一遍後,會出現一個問題,就是一個位置上的數的長度增加以後,會對前面的某些數產生影響。這時候就對每對括號的左右端點,檢查端點數字的實際長度與記錄的端點數字長度是否相同
#include<cstdio> #include<algorithm> #include<cmath> #include <cstring> #include <iostream> #include<map> using namespace std; const int maxn = 4050; int n, numl[maxn*15]; int la[maxn], lb[maxn]; int sta[maxn], L[maxn], R[maxn]; char s[maxn]; void init() { numl[0] = 0; for(int i = 1;i < maxn*15;i++) numl[i] = numl[i/10] + 1; } int main() { init(); scanf("%s", s); n = strlen(s); for(int i = 0;i < n;i++) la[i] = lb[i] = 1; while(1) { int top = 0, cur = 0; for(int i = 0;i < n;i++) { if(s[i] == '(') { cur += la[i] + lb[i] + 2; L[i] = cur; sta[top++] = i; } else R[sta[--top]] = cur; } bool flag = 1; for(int i = 0;i < n;i++) { if(s[i] == '(') { if(numl[L[i]] != la[i] || lb[i] != numl[R[i]]) { flag = 0; break; } } } if(flag) break; for(int i = 0;i < n;i++) { if(s[i] == '(') la[i] = numl[L[i]], lb[i] = numl[R[i]]; } } for(int i = 0;i < n;i++) { if(s[i] == '(') printf("%d,%d:", L[i], R[i]); } printf("\n"); return 0; }
C - Greetings!
有n種大小不同的矩形,現在需要選擇k種任意大小的矩形,它們全部覆蓋住,使得浪費的面積最小。
1<=n,k<=15,一開始想搜尋,然後就TLE了……
正解是狀壓dp,dp[i][S]表示用i種矩形覆蓋S這個集合浪費的最小面積。
for(int k = p;k;k = (k-1)&p) 這個操作可以列舉二進位制p的所有子集,列舉所有方案的所有子集的時間複雜度是。學到了Orz
#include <algorithm> #include <cmath> #include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const int maxn = 16; const ll INF = (1LL << 62) - 1; int n, k; ll dp[maxn][1<<maxn], val[1<<maxn], sum[1<<maxn]; int H[1<<maxn], W[1<<maxn], num[1<<maxn]; struct node { int h, w; int cnt; }e[maxn]; int main() { scanf("%d%d", &n, &k); for(int i = 0;i < n;i++) { scanf("%d%d%d", &e[i].w, &e[i].h, &e[i].cnt); } memset(sum, 0, sizeof(sum)); for(int i = 0;i < (1<<n);i++) { for(int j = 0;j < n;j++) { if((i>>j) & 1) { H[i] = max(H[i], e[j].h); W[i] = max(W[i], e[j].w); sum[i] += 1LL*e[j].h*e[j].w*e[j].cnt; num[i] += e[j].cnt; } } val[i] = 1LL*H[i]*W[i]*num[i] - sum[i]; } for(int i = 0;i <= k;i++) { for(int j = 0;j < (1<<n);j++) dp[i][j] = INF; } dp[0][0] = 0; for(int i = 1;i <= k;i++) { for(int j = 0;j < (1<<n);j++) { ll tmp = INF; for(int p = j;p;p = (p-1) & j) { if(dp[i-1][j-p] < INF) tmp = min(tmp, dp[i-1][j-p] + val[p]); } dp[i][j] = tmp; } } ll ans = INF; for(int i = 0;i <= k;i++) ans = min(ans, dp[i][(1<<n)-1]); printf("%I64d\n", ans); return 0; }
E - K-Inversions
給出一個只含有A和B的字串,定義如果si為B,sj為A, 且j-i=k,這就產生了一個k-Inversion。求1~n-1的Inversion數目。
考慮當j > i時,也就是說,j + (n-i) > n。構造兩個多項式,A[i] = (s[i] == 'B'),B[n-i-1] = (s[i] == 'A'),FFT加速卷積取n~n*2-1項的係數即可。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;
const ll INF = (1LL << 62) - 1;
const double pi = acos(-1.0);
const int maxn = 6000050;
int n, len, ans[maxn];
char s[maxn];
struct Complex
{
double x, y;
Complex (double _x = 0.0, double _y = 0.0)
{
x = _x;
y = _y;
}
Complex operator - (const Complex &o) const
{
return Complex(x - o.x, y - o.y);
}
Complex operator + (const Complex &o) const
{
return Complex(x + o.x, y + o.y);
}
Complex operator * (const Complex &o) const
{
return Complex(x*o.x - y*o.y, x*o.y + y*o.x);
}
}a[maxn], b[maxn];
void change(Complex y[], int len)
{
int i, j, k;
for(i = 1, j = len/2;i < len-1;i++)
{
if(i < j) swap(y[i], y[j]);
k = len/2;
while(j >= k)
{
j -= k;
k /= 2;
}
if(j < k) j += k;
}
}
void FFT(Complex y[], int len, int op)
{
change(y, len);
for(int h = 2;h <= len;h <<= 1)
{
Complex wn(cos(-op*2*pi/h), sin(-op*2*pi/h));
for(int j = 0;j < len;j += h)
{
Complex w(1, 0);
for(int k = j;k < j + h/2;k++)
{
Complex u = y[k];
Complex t = w*y[k + h/2];
y[k] = u + t;
y[k + h/2] = u - t;
w = w*wn;
}
}
}
}
int main()
{
scanf("%s", s);
n = strlen(s);
len = 1;
while(len < n*2) len <<= 1;
for(int i = 0;i < n;i++)
{
if(s[i] == 'A')
{
a[n-i-1] = Complex(0, 0);
b[i] = Complex(1, 0);
}
else
{
a[n-i-1] = Complex(1, 0);
b[i] = Complex(0, 0);
}
}
for(int i = n;i < len;i++)
a[i] = b[i] = Complex(0, 0);
FFT(a, len, 1);
FFT(b, len, 1);
for(int i = 0;i < len;i++)
a[i] = a[i]*b[i];
FFT(a, len, -1);
for(int i = 0;i < len;i++)
ans[i] = (int)(a[i].x/len + 0.5);
for(int i = 0;i < n - 1;i++)
printf("%d\n", ans[i + n]);
return 0;
}
F - Mountain Scenes
將n個1*1方塊堆到h*w格子裡,問有多少種不同的出現山峰的方案。
dp[i][j]表示前i列放了j個的方案數,dp[i][j+k] = Sum{dp[i-1][j]}轉移,求出所有的方案數,最後減去沒有山峰的種情況即可。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;
const ll INF = (1LL << 62) - 1;
const ll mod = 1e9 + 7;
int n, w, h;
ll dp[105][10005];
int main()
{
scanf("%d%d%d", &n, &w, &h);
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for(int i = 1;i <= w;i++)
{
for(int j = 0;j <= n;j++)
{
if(dp[i-1][j] == 0) continue;
for(int k = 0;k <= h;k++)
{
if(j + k > n) break;
dp[i][j+k] += dp[i-1][j];
dp[i][j+k] %= mod;
}
}
}
ll ans = 0;
for(int i = 0;i <= n;i++)
ans = (ans + dp[w][i]) % mod;
for(int i = 0;i <= h;i++)
{
if(i*w <= n)
ans--;
}
ans = (ans + mod) % mod;
printf("%I64d\n", ans);
return 0;
}
I - Tourists
在樹上求k到k的倍數的路徑長度之和。
直接暴力列舉所有的邊用LCA計算即可。時間複雜度。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;
const int maxn = 200050;
const ll INF = (1LL << 62) - 1;
int n, a, b, no;
int pre[maxn][35], dep[maxn];
int head[maxn];
bool vis[maxn];
struct node
{
int to;
int nxt;
}e[maxn << 1];
void add(int a, int b)
{
e[no].to = b, e[no].nxt = head[a];
head[a] = no++;
e[no].to = a, e[no].nxt = head[b];
head[b] = no++;
}
void dfs(int u, int fa, int num)
{
pre[u][0] = fa;
dep[u] = num;
vis[u] = 1;
for(int i = head[u];i != -1;i = e[i].nxt)
{
int v = e[i].to;
if(vis[v]) continue;
dfs(v, u, num + 1);
}
return;
}
void init_lca()
{
for(int j = 1;(1<<j) <= n;j++)
{
for(int i = 1;i <= n;i++)
pre[i][j] = -1;
}
for(int j = 1;(1<<j) <= n;j++)
{
for(int i = 1;i <= n;i++)
{
if(pre[i][j - 1] != -1)
pre[i][j] = pre[pre[i][j - 1]][j - 1];
}
}
}
int lca(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
int mlg = 0;
while((1<<mlg) <= dep[x]) mlg++;
mlg--;
for(int i = mlg;i >= 0;i--)
{
if(dep[x] - (1<<i) >= dep[y])
x = pre[x][i];
}
if(x == y) return x;
for(int i = mlg;i >= 0;i--)
{
if(pre[x][i] != -1 && pre[x][i] != pre[y][i])
x = pre[x][i], y = pre[y][i];
}
return pre[x][0];
}
int main()
{
scanf("%d", &n);
no = 0;
memset(head, -1, sizeof(head));
for(int i = 1;i < n;i++)
{
scanf("%d%d", &a, &b);
add(a, b);
}
dfs(1, 0, 1);
ll ans = 0;
init_lca();
for(int i = 1;i <= n;i++)
{
for(int j = i*2;j <= n;j += i)
{
int tp = lca(i, j);
ans += 1LL*(dep[i] - dep[tp] + dep[j] - dep[tp] + 1);
}
}
printf("%I64d\n", ans);
return 0;
}