[題目鏈接](http://acm.split.hdu.edu.cn/search.php?field=problem&key=2016+Multi-University+Training+Contest+4&source=1&searchmode=source)
A.Another Meaning(HDU 5763)
大意
給定一個句子
思路
考慮句子的前綴
代碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10, mod = 1e9 + 7;
bool ok[maxn];
char s[maxn], p[maxn];
int t, sn, pn, nxt[maxn], d[maxn];
// 計算next數組
void getNext() {
int j, k;
nxt[j=0] = k = -1;
while(j < pn) {
if(k == -1 || p[k] == p[j]) {
nxt[++j] = ++k;
}
else {
k = nxt[k];
}
}
}
// KMP算法
void kmpCount() {
int j = 0, ans = 0;
for(int i = 0; i < sn; i++) {
while(j && s[i] != p[j]) {
j = nxt[j];
}
if(s[i] == p[j]) {
j++;
}
if(j == pn) {
ok[i-pn+1] = true;
j = nxt[j];
}
}
}
int main() {
scanf("%d", &t);
for(int kase = 1; kase <= t; kase++) {
memset(ok, 0, sizeof(ok));
memset(d, 0, sizeof(d));
scanf("%s%s", s, p);
sn = strlen(s);
pn = strlen(p);
getNext();
kmpCount();
for(int i = 0; i < sn; i++) {
if(ok[i] == true) {
d[i+pn-1] = (d[i+pn-1] + (i > 0 ? d[i-1] : 1)) % mod;
}
d[i] = (d[i] + (i > 0 ? d[i-1] : 1)) % mod;
}
printf("Case #%d: %d\n", kase, d[sn-1]);
}
return 0;
}
E.Lucky7(HDU 5768)
大意
給定一個區間
思路
本題實際上要我們求對於一個給定的
notOk(r)相當於集合“
由於
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 20;
ll T, n, l, r, p[maxn], a[maxn], m[maxn], b[maxn];
// 快速冪算法
ll modMul(ll a, ll b, ll mod) {
ll ans = 0;
for(; b > 0; b >>= 1) {
if (b & 1) {
ans = (ans + a) % mod;
}
a = (a << 1) % mod;
}
return ans;
}
// 擴展歐幾里得算法
void extGcd(ll a, ll b, ll& x, ll& y) {
if(b == 0) {
x = 1;
y = 0;
return;
}
extGcd(b, a % b, x, y);
ll tmp = x;
x = y;
y = tmp - (a / b) * y;
}
// 中國剩餘定理
ll CRT(ll a[], ll m[], ll M, ll n) {
ll ans = 0;
for(int i = 0; i <= n; i++) {
ll x, y, Mi = M / m[i];
extGcd(Mi, m[i], x, y);
x = (x % M + M) % M;
ans = (ans + modMul(modMul(Mi, x, M), a[i], M)) % M;
}
return (ans + M) % M;
}
// 計算(0, r]內有多少滿足條件的數
ll count(ll r) {
if(r < 0) {
return 0;
}
ll x, num, ans = r / 7;
// 狀態壓縮的容斥原理
for(int mask = 1; mask < (1 << n); mask++) {
ll tail = 0, cnt = 0, M = 7;
for(int j = 0; j < n; j++) {
if(mask & (1 << j)) {
m[++tail] = p[j];
b[tail] = a[j];
M *= m[tail];
cnt++;
}
}
x = CRT(b, m, M, tail);
if(r < x) {
continue;
}
num = (r - x) / M + 1;
ans += (cnt % 2 ? -1 : 1) * num;
}
return ans;
}
int main() {
cin >> T;
m[0] = 7;
for(int kase = 1; kase <= T; kase++) {
cin >> n >> l >> r;
for(int i = 0; i < n; i++) {
cin >> p[i] >> a[i];
}
cout << "Case #" << kase << ": ";
cout << count(r) - count(l - 1) << endl;
}
return 0;
}
F.Substring(HDU 5769)
大意
求字符串
思路
如果是求字符串
現還要滿足子串中含有字符
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
char s[maxn];
int sa[maxn], t1[maxn], t2[maxn], c[maxn];
int Rank[maxn], height[maxn];
char ch[5];
int nxt[maxn];
// 計算高度數組
void getHeight(int n){
int k = 0;
for(int i=1;i<=n;i++)Rank[sa[i]] = i;
for(int i=0;i<n;i++){
if(k)k--;
int j = sa[Rank[i]-1];
while(s[i+k]==s[j+k])k++;
height[Rank[i]] = k;
}
}
bool cmp(int *r,int a,int b,int l){
return (r[a]==r[b] && r[a+l]==r[b+l]);
}
// 計算後綴數組
void build_sa(int m,int n){
int i,*x=t1,*y=t2,k,p;
for( i=0;i<m;i++)c[i] = 0;
for( i=0;i<n;i++)c[x[i] = s[i]]++;
for( i=1;i<m;i++)c[i] += c[i-1];
for( i=n-1;i>=0;i--)sa[-- c[x[i]]] = i;
for(k=1,p=0;p<n;m=p,k<<=1){
p = 0;
for(i=n-k;i<n;i++)y[p++] = i;
for(i=0;i<n;i++)if(sa[i]>=k)y[p++] = sa[i]-k;
for(i=0;i<m;i++)c[i] = 0;
for(i=0;i<n;i++)c[x[y[i]]]++;
for(i=1;i<m;i++)c[i] += c[i-1];
for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]] = y[i];
swap(x,y);
p = 1; x[sa[0]] = 0;
for(i=1;i<n;i++)
x[sa[i]] = cmp(y,sa[i-1],sa[i],k)?p-1:p++;
}
getHeight(n-1);
}
// 計算不同子串的個數
ll solve(int n) {
int last = n;
ll ans = n - sa[0];
// 預處理上文提到的next數組
for(int i = n; i >= 0; i--) {
if(s[i] == ch[0]) {
last = i;
}
nxt[i] = last;
}
for(int i = 1; i <= n; i++) {
ans += n - max(nxt[sa[i]], sa[i] + height[i]);
}
return ans;
}
int main(){
int T;
scanf("%d", &T);
for(int kase = 1; kase <= T; kase++) {
scanf("%s%s", ch, s);
int n = strlen(s);
build_sa(255, n + 1);
printf("Case #%d: %I64d\n", kase, solve(n));
}
return 0;
}
J.The All-purpose Zero(HDU 5773)
大意
題給一個數字序列,其中的每個數字
思路
假設我們將序列
那麼問題來了,如果將
綜上所述,算法就是將
代碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int t, n, zero, num, tail, idx, ans, a[maxn], b[maxn], c[maxn];
int main() {
scanf("%d", &t);
for(int kase = 1; kase <= t; kase++) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
zero = num = 0;
tail = 0;
// 構造消去0後的新序列b
for(int i = 1; i <= n; i++) {
if(a[i] > 0) {
b[++tail] = a[i] - num;
num = 0;
}
else {
zero++;
num++;
}
}
ans = 0;
fill(c + 1, c + n + 1, INT_MAX);
c[0] = -INT_MAX;
// 求b的LIS
for(int i = 1; i <= tail; i++) {
idx = lower_bound(c + 1, c + n + 1, b[i]) - c;
ans = max(ans, idx);
c[idx] = b[i];
}
printf("Case #%d: %d\n", kase, ans + zero);
}
return 0;
}
K.Where Amazing Happens(HDU 5774)
思路
給出
思路
先編寫一個輔助程序,將答案製成表格。然後在主程序中根據輸入輸出即可。
代碼
#include <bits/stdc++.h>
using namespace std;
// 打表需要的輔助程序
/*
int x, y;
string s;
map <string, int> mp;
map <string, int> :: iterator it;
int main() {
freopen("data.txt", "r", stdin);
freopen("output.txt", "w", stdout);
while(scanf("%d-%d ", &x, &y) == 2) {
getline(cin, s);
mp[s]++;
}
for(it = mp.begin(); it != mp.end(); it++) {
cout << "mp[\"" << it->first << "\"] = " << it->second << ";\n";
}
return 0;
}
*/
///*
int t;
string s;
map <string, int> mp;
// 根據輔助程序的輸出結果製表
void init() {
mp["Baltimore Bullets"] = 1;
mp["Boston Celtics"] = 17;
mp["Chicago Bulls"] = 6;
mp["Cleveland Cavaliers"] = 1;
mp["Dallas Mavericks"] = 1;
mp["Detroit Pistons"] = 3;
mp["Golden State Warriors"] = 2;
mp["Houston Rockets"] = 2;
mp["L.A. Lakers"] = 11;
mp["Miami Heat"] = 3;
mp["Milwaukee Bucks"] = 1;
mp["Minneapolis Lakers"] = 5;
mp["New York Knicks"] = 2;
mp["Philadelphia 76ers"] = 2;
mp["Philadelphia Warriors"] = 2;
mp["Portland Trail Blazers"] = 1;
mp["Rochester Royals"] = 1;
mp["San Antonio Spurs"] = 5;
mp["Seattle Sonics"] = 1;
mp["St. Louis Hawks"] = 1;
mp["Syracuse Nats"] = 1;
mp["Washington Bullets"] = 1;
}
int main() {
init();
scanf("%d\n", &t);
for(int kase = 1; kase <= t; kase++) {
getline(cin, s);
cout << "Case #" << kase << ": " << mp[s] << endl;
}
return 0;
}
//*/
L.Bubble Sort(HDU 5775)
大意
給定一個排列並對這個排列進行冒泡排序,求該排列中每個元素在冒泡排序的過程中將會達到的最右位置和最左位置的差值。
思路
爲了方便討論,將排列中的數分成兩種
- 排序後的位置在排序前的位置的左邊。那麼它的目的位置就是它能到達的最左邊的位置,它能到達的最右邊的位置是它的位置
i 加上在它右邊的比它小的數的個數rightLess[i] 。 - 排序後的位置在排序前的位置的右邊。那麼它的起始位置就是它能到達的最左邊的位置,它能到達的最右邊的位置是它的位置
i 加上在它右邊的比它小的數的個數rightLess[i] 。
代碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int t, n, a[maxn], b[maxn], bit[maxn];
// 樹狀數組模板
void add(int x) {
for(; x <= n; x += x & -x) {
bit[x]++;
}
}
int sum(int x) {
int res = 0;
for(; x >= 1; x -= x & -x) {
res += bit[x];
}
return res;
}
int main() {
scanf("%d", &t);
for(int kase = 1; kase <= t; kase++) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
memset(bit, 0, sizeof(bit));
for(int i = n; i >= 1; i--) {
// 計算差值
b[a[i]] = i + sum(a[i]) - min(i, a[i]);
add(a[i]);
}
printf("Case #%d:", kase);
for(int i = 1; i <= n; i++) {
printf(" %d", b[i]);
}
puts("");
}
return 0;
}
(其它題目略)