這些東西發上來沒壞事。
未完待續=。=......
chrous:
題意:略
傻Xdp,直接設計兩個狀態f[ i , j], g[ i , j]分別表示形成i~j一段,最後放得在最左/最右的方案數,直接轉移即可。
# include <cstdlib>
# include <cstdio>
# include <cmath>
using namespace std;
const int mo = 19650827, maxn = 1000+10;
int f[maxn][maxn], g[maxn][maxn];
int i, j, l, r, n, h[maxn];
int main()
{
freopen("chorus.in", "r", stdin);
freopen("chorus.out", "w", stdout);
scanf("%d", &n);
for (i = 1; i <= n; i++) scanf("%d", &h[i]);
for (i = 1; i <= n; i++) f[i][i] = 1;
for (r = 2; r <= n; r++)
for (l = r-1; l >= 1; l--)
{
f[l][r] = ((h[l]<h[l+1])*f[l+1][r] + (h[l]<h[r])*g[l+1][r]) % mo;
g[l][r] = ((h[r]>h[l])*f[l][r-1] + (h[r]>h[r-1])*g[l][r-1]) % mo;
}
printf("%d", (f[1][n]+g[1][n])% mo);
return 0;
}
planar:
題意:給定一個圖的哈密頓迴路和所有的邊,問其是否可能是平面圖;點n <= 200, 邊m<=10000,100組數據。
利用題目給出的哈密頓迴路(我們需要其實只要哈密頓路徑就可以了)
想象把哈密頓路徑“扯直”,那麼其餘所有的邊都必定分居路徑的兩側。
如果兩條邊放在同一側會相交,那麼他們必定放在異側,最後判斷是否矛盾。
2-sat,甚至簡單的並查集都可以解決這個問題。
唯一的問題是o(m^2)的枚舉邊是否相交太慢了,一直想找到合適的方法優化,結果…….結果…….原來平面圖o(m) = o(n)(被坑了=。=!), 實際上m <= 3*n-6 =.=!,那麼判一判,直接裸就行了!
# include <cstdlib>
# include <cstdio>
# include <cmath>
# include <cstring>
using namespace std;
const int maxn = 10000 + 5;
int ufs[maxn*4], rel[maxn*4];
int g[maxn], x[maxn*4], y[maxn*4], tmp;
int n, m;
int find (int x)
{
int y;
if (ufs[x] == x) return x;
else {
y = find(ufs[x]);
rel[x] = rel[x] ^ rel[ufs[x]];
return ufs[x] = y; };
}
bool work()
{
int fi, fj, i, j, k;
for (i = 1; i<= m; i++) if (g[x[i]] > g[y[i]]) tmp = x[i], x[i] = y[i], y[i] = tmp;
for (i = 1; i<= m; i++) ufs[i] = i, rel[i] = 0;
for (i = 1; i<= m; i++)
for (j = 1; j <= m; j++)
if (i != j)
if (g[x[i]] < g[x[j]] && g[x[j]] < g[y[i]] && g[y[i]] < g[y[j]])
{
fi = find(i); fj = find(j);
if (fi != fj) ufs[fi] = fj, rel[fi] = 1^rel[i]^rel[j];
else if ((rel[i]^rel[j]) == 0)
return false;
}
return true;
}
int main()
{
int test, i, k;
freopen("planar.in", "r", stdin);
freopen("planar.out", "w", stdout);
scanf("%d", &test);
for (int ssss = 1; ssss <= test; ssss++)
{
scanf("%d%d", &n, &m);
memset(g, 0, sizeof(g));
memset(x, 0, sizeof(x));
memset(y, 0, sizeof(y));
memset(ufs, 0, sizeof(ufs));
memset(rel, 0, sizeof(rel));
int xx, yy, now = 0;
for (i = 1; i<= m; i++)
{
scanf("%d%d", &xx, &yy);
if (xx != yy)x[++now]=xx, y[now]=yy;
}
m = now;
for (i = 1; i<= n; i++)
{
scanf("%d", &k);
g[k] = i;
};
if (m > 3*n) printf("NO\n");
else if (work()) printf("YES\n"); else printf("NO\n");
}
return 0;
}
fsk: 暫未做=。=!......題目都木有看懂=。=
bus:
題意: 公路上有1~n個站,k個公交車,1~k是他們的起點站,n-k+1~n是重點站。公交車從編號小的站開往編號大的站,一個站有且僅有一個公交車停靠,對於一個公交車,它停靠的任意兩個站之間,編號距離不得超過p;
K,p <= 10, n <=10^9;
n相當大,k相當小,不用想就是狀壓矩乘了=。=!
但是如果直接記錄每輛這距離當前的距離,那麼信息量so 大了;
注意到公交車是無差別的,所以我們只需要記錄距離當前位置p以內的所有公交車的距離就行了......有點繞。
反正狀態被壓縮到了C(9,5)以內,可以狀壓矩乘了。
# include <cstdlib>
# include <cstdio>
# include <cmath>
# include <cstring>
using namespace std;
const int size = 130, mo = 30031;
bool a[size][20];
int t[size][size], ml[size][size], c[size][size];
int top = 1, n, kn, p;
void dfs(int have, int past)
{
int i;
if (have == kn)
{
top++;
for (i = 1; i <= kn; i++) a[top][i] = a[top-1][i];
}
else
for (i = past+1; i <= p; i++)
{
a[top][i] = true;
dfs(have+1, i);
a[top][i] = false;
}
}
void prepare()
{
int flag ; int i, j, k;
for (i = 1; i <= top; i++)
for (j = 1; j <= top; j++)
{
flag = 1;
for (k = 1; k <= p; k++)
if ((a[i][k]^a[j][k+1])) flag --;
if (flag == 0) c[i][j] = ml[i][j] = 1;
}
}
void mul(int c[size][size], int a[size][size], int b[size][size])
{
int i, j, k;
memset(t, 0, sizeof(t));
for (i = 1; i <= top; i++)
for (j = 1; j <= top; j++)
for (k = 1; k <= top; k++)
t[i][j] = (t[i][j]+a[i][k]*b[k][j]) % mo;
for (i = 1; i <= top; i++)
for (j = 1; j <= top; j++)
c[i][j] = t[i][j];
}
int main()
{
freopen("bus.in", "r", stdin);
freopen("bus.out", "w", stdout);
scanf("%d%d%d", &n, &kn, &p);
a[top=1][1] = true; dfs(1, 1);
top--;prepare();
for (n-=kn+1; n >0; n >>=1, mul(ml,ml,ml))
if (n & 1) mul(c,c,ml);
// for (n-=kn+1; n >0; n--)
// mul(c,c,ml);
printf("%d", c[1][1]);
return 0;
}
stone:
題意:給定n堆石子,實現有幾堆的石子被取走了,兩人博弈取石子,一堆石子被取當且僅當它左邊或右邊的被取走。問先後手分別能去多少石子。
看到這道題,似曾相識中=。=,貌似去年一次考試出現了這個題目,那個時候是聽了劉堯學長解釋了好久才明白,最後也寫得暈暈的。
仔細想了想大致回想起了思路, 統計兩選手石子數目之差。取走的石子將這個序列分成了若干塊,最理想的情況是:
↗, ↘↗,….., ↘↗, ↘,這樣的情況直接貪心取就ok了;
但是情況肯定要複雜的多,但是 可以修復。
對於↘↗被破壞(單純的↘或↗也可看做↘↗), 一定出現了↗↘的情況,即a[ i -1]< a[ i] , a[i]> a[i + 1], 對於這種情況,“先手”必取a[i- 1] 和a[i + 1], 後手必取a[I], 這樣,三個數可以合併成a[i+1]+a[i-1]-a[i]; 而對於左右兩邊的 ↗和↘, 分別有 ↘,↗的情況與之破壞,這樣的情況“先手”取必虧,大家都不願意先取它,所以一定會留到最後,那麼,誰回先取到它們可以推算出來,這樣,它們可以提前統計出來,對於最後的石子,排序一個個取就可以了。
# include <cstdlib>
# include <cstdio>
using namespace std;
const long long oo= (long long)1 << 62;
const int maxn = 1000000+200;
long long sum, ans;
long long a[maxn], b[maxn];
int n, tot, next[maxn], pred[maxn];
long long tmp;
void del(int v)
{
next[pred[v]] = next[v];
pred[next[v]] = pred[v];
}
void maintain(int p)
{
if ((a[p] != -oo && a[pred[p]] != -oo && a[next[p]]!= -oo) &&(a[p] >= a[pred[p]] && a[p] >= a[next[p]]))
{
a[p] = a[pred[p]] + a[next[p]] - a[p]; a[pred[p]] = a[next[p]] = -oo;
del(pred[p]); del(next[p]);
maintain(pred[p]); maintain(p); maintain(next[p]);
}
}
void change(int i, int j)
{
for (;b[i] != -oo && b[i+j] != -oo && b[i] > b[i+j]; b[i]=b[i+j]=-oo, i+=2*j)
if (!(tot & 1)) ans += b[i+j]-b[i]; else ans -= b[i+j]-b[i];
}
void sort(int l, int r)
{
int i = l, j = r; long long d = b[(l+r)>>1];
for (;i <= j;)
{
for (;b[i] > d; i++);
for (;b[j] < d; j--);
if (i <= j) tmp = b[i], b[i] = b[j], b[j] = tmp, i++, j--;
}
if (i < r) sort(i, r);
if (l < j) sort(l, j);
}
int main()
{
int i, j; bool t1 = false, t2 = false;
freopen("stone.in", "r", stdin);
freopen("stone.out", "w", stdout);
scanf("%d", &n); a[0] = -oo; a[n+1] = -oo;
for (i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
sum += a[i];
if (!a[i]) a[i] = -oo; else ++tot;
}
if (a[1] == -oo) t1 = true; if (a[n] == -oo) t2 = true;
for (i = 1; i <= n; i++) pred[i] = i-1, next[i] = i+1;
for (i = 1; i <= n; i++)
if (a[i] != -oo) maintain(i);
for (b[0] = -oo, i = 1, j = 0; i <= n; i=next[i])
{
if (a[i] != -oo) b[++j] = a[i];
else if (b[j] != -oo) b[++j] = -oo;
}
for (;b[j] == -oo;j--); n = j;
b[0] = -oo; b[n+1] = -oo;
if (!t1)change(1, 1);
if (!t2)change(n, -1);
sort(1, n);
for (i = 1; i <= n && b[i] != -oo; i++)
if (i & 1) ans += b[i]; else ans -= b[i];
printf("%I64d %I64d", sum+ans >> 1, sum-ans >> 1);
return 0;
}
city
......
Bounce:
題意:給定序列k[n],兩個操作,一個修改操作,修改某個位置的k,一個詢問操作,詢問從i位置開始,每次跳躍到i+k[I], 問最後跳多少次可以跳出序列。
正解要用動態樹的,結果無恥的用塊鏈水過了。
把k分成sqrt(n), 統計每個位置跳出該塊的步數以及跳出去會跳到哪裏,這樣每個詢問都是sqrt(n)級別的,速度勉勉強強;
# include <cstdio>
# include <cstdlib>
# include <cmath>
using namespace std;
const int maxn = 300000;
int aux[maxn], k[maxn], head[maxn], tail[maxn], go[maxn], cost[maxn];
int n, m, size, num;
inline void change(int st, int d)
{
int i, id = aux[st]; k[st] = d;
for (i = st; i >= head[id]; i--)
if (i+k[i] > tail[id]) cost[i] = 1, go[i] = i+k[i];
else go[i] = go[i+k[i]], cost[i] = cost[i+k[i]]+1;
}
void prepare()
{
int i, j;
size = (int) floor(sqrt(n))+1;
for (i = 1, num = 0; i <= n; i+= size)
head[++num] = i, tail[num] = i+size-1;
tail[num] = n;
for (i = 1; i <= num; i++)
for (j = head[i]; j <= tail[i]; j++)
aux[j] = i;
for (i = 1; i <= num; i++)
change(tail[i], k[tail[i]]);
}
inline int ask(int st)
{
int ans;
for (ans = 0; st <= n; st = go[st])
ans += cost[st];
return ans;
}
int main()
{
int st, c, i, d;
freopen("bounce.in", "r", stdin);
freopen("bounce.out", "w", stdout);
scanf("%d", &n);
for (i = 1; i <= n; i++) scanf("%d", &k[i]);
prepare();
scanf("%d", &m);
for (i = 1; i <= m; i++)
{
scanf("%d", &c);
if (c == 1)
{
scanf("%d", &st); st++;
printf("%d\n", ask(st));
}
else
{
scanf("%d%d", &st, &d); st++;
change(st, d);
}
}
return 0;
}
Matrix:
題意:給定b[N][N], b[I][J]= a[i][j]+a[i-1][j]+a[i][j-1]+a[i-1][j-1], 求字典序最小的A
A的元素不超過10, n<= 200;
此題無恥的抄了別人的一個關於b,a和輔助數組c的公式:
Ai,j= Ci,j + (-1)^i+j-2 *A1,1 + (-1)^i-1*A1,j + (-1)^j-1*Ai,1 (i>1,j>1)
其中c[I][J] = B[I][J]-C[I-1][J]-C[I][J-1]-C[I-1][J-1];
那麼,搜索第一行的數值,維護第一列的取值範圍,出現矛盾則剪枝,這樣搜就可以了。
吐槽:公式抄了別人的,結果那個公式的指數居然是錯的,TAT,結果一直沒調出來,wa,wa,wa,wa…………最後手算檢驗才發現公式的指數不對=。=!,抄別人的式子真不是好習慣。