E. Not Same (2s 256Mb)
題目大意
n(1e3)個ai,表示第i處有ai個塊。每次可以選擇下標的一個子集,使子集內的塊減1,不能減塊數爲0的位置。要求選擇n+1個子集至多,且使用子集兩兩不同。輸出任意一個構造答案。
樣例輸入輸出
分析(to be continue)
一個2500的題在賽後我看的時候被我數十分鐘一己之力幹掉,一發入魂,還和題解方法不一樣,還是挺吊的,雖然我都不太明白我的做法爲什麼對。。。
通過一番在草稿紙上發揮,我覺得應該對每個ai構造答案每一列。不知道怎麼,我就得出按降序排列ai去求解。。。然後我就奇思妙想到
代碼
int main()
{
n = read();
for(int i = 1; i <= n; i++)
{
a[i] = {i, read()};
}
sort(a+1, a+n+1);
for(int i = 1; i <= n; i++)
{
int pos = a[i].id, j = i, sum = a[i].v;
while(sum--)
{
ans[j][pos] = 1;
j = j%(n+1)+1;
}
}
printf("%d\n", n+1);
for(int i = 1; i <= n+1; i++)
{
for(int j = 1; j <= n; j++) putchar(ans[i][j]+'0');
putchar(10);
}
return 0;
}
D. Wrong Answer on test 233 (Easy/Hard Version) (1s 256Mb)
題目大意
n(2000/2e5)個選擇題,每個都有k種選項。給出hi爲第i個題的答案,求有多少種答題方案,使得此方案向右移動一位(n->1)去答題比原情況得分嚴格高。mod 998244353。
分析
Easy:n2dp表示種數,一維前i位,一維得分差,n*n轉移。
Hard:設移動後得分減移動前得分爲d,每個位置選擇答案爲ai。對於每個位置,如果,則無論如何設置,對d沒有影響。否則可以使使得d加1,也可以使使得d減1,其他設置ai對d無影響。設可有影響的題數爲t。枚舉t裏有i題對答案無影響,則答案爲。不能影響題隨便選擇,可有但無從k-2種裏面選擇,剩餘組合數學。
強大的wzgy的代碼教會我們,每一種d>0的情況,都一一對應一種d<0的情況,所以只需將所有情況計算出來,減去平局d=0,再除以2即可,wow!
代碼
int main()
{
n = read(), k = read();
pre();
for(int i = 1; i <= n; i++) a[i] = read();
if(k == 1) //特判k=1時
{
printf("0\n");
return 0;
}
for(int i = 1; i <= n; i++) if(a[i] != a[i%n+1]) ab++;//計算可改變d的題目數量
int ans = 0;
for(int i = 0; i*2 <= ab; i++) //求解平局情況
{
ans = (ans + 1LL*C(ab, i*2)*C(i*2, i)%mod*km(k-2, ab-i*2)%mod)%mod;
}
ans = 1LL*(km(k, ab)-ans+mod)*inv[2]%mod; //所以情況減去平局除以2
ans = 1LL*ans*km(k, n-ab)%mod; //最後乘上不可改變d的題的每題k個隨便選
printf("%d\n", ans);
return 0;
}
C. Arson In Berland Forest (2s 512Mb)
題目大意
'.' 和 'X' 圖 ,'X'表示已被火燒的樹,'.'則表示未被燒。燃燒情況是每棵燃燒的樹通過1秒將八個方向的樹燒着。構造一個可行答案,要求經過時間T最大,並輸出第0時刻已經燒着的樹是哪些,這些樹的數量沒有限制。
樣例輸入輸出
分析
顯然二分時間T,關鍵在於check。從下到上,從右到左,求解右下方可燃燒範圍。如果>=2*T+1,可以在右+T下+T放初始燃燒樹。最後判斷給個每棵樹是不是都被放置初始燃燒樹燃燒。判斷時,可以用一個巧方法,將燃燒等價於從左上角往右下燃燒2*T+1,所以判斷時可以看左上三個方向那個剩餘燃燒範圍最大。
題解說也可以用bfs,判斷某個點是否最近的'.'的距離大於T,使用一開始一遍bfs。不是太懂,懵比。
代碼
int ma[maxn], vis[maxn];
int check(int x)
{
memset(vis, 0, sizeof(vis));
int s = x<<1|1;
for(int i = 1; i <= n; i++) //放置初始燃燒樹
for(int j = 1; j <= m; j++) if(ma[cor(i, j)] >= s) vis[cor(i, j)] = s;
for(int i = 1; i <= n; i++) //將所有可燒的樹燒掉
for(int j = 1; j <= m; j++)
vis[cor(i, j)] = max(vis[cor(i, j)], -1+max(vis[cor(i-1, j-1)], max(vis[cor(i-1, j)], vis[cor(i, j-1)])));
for(int i = 1; i <= n; i++) //判斷是否所有的樹都被燒到
for(int j = 1; j <= m; j++) if(a[cor(i, j)] && vis[cor(i, j)] <= 0) return 0;
return 1;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
getchar();
for(int j = 1; j <= m; j++) a[cor(i, j)] = getchar() == 'X';
}
for(int i = n; i; i--) //求解最遠燃燒距離
for(int j = m; j; j--)
{
if(a[cor(i, j)])
{
ma[cor(i, j)] = 1 + min(ma[cor(i+1, j+1)], min(ma[cor(i+1, j)], ma[cor(i, j+1)]));
}
}
int l = 0, r = 1e6; // r可以是min(n, m)
while(l != r)
{
int mid = l + ((r-l) >> 1) + 1;
if(check(mid)) l = mid;
else r = mid-1;
}
int ans = l;
printf("%d\n", ans);
for(int i = 1; i <= n; i++) //構造答案
for(int j = 1; j <= m; j++) if(ma[cor(i, j)] >= ans*2+1) lis[cor(i+ans, j+ans)] = 1;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++) putchar(lis[cor(i, j)] ? 'X' : '.');
putchar(10);
}
return 0;
}
B. Optimal Subsequences (Easy/Hard Version) (3s 256Mb)
題目大意
n(100/2e5)個ai(1e9)有序,m(100/2e5)個詢問,每個詢問有k(1-n)和pos(1-k),求將前k大的ai按原序排列(即subsequence)(如果相同,取字典序最小),問此序列中第pos個元素是多少。
分析
Easy:暴力構造所有子序列即可。
Hard:線段樹。離線按k排序詢問。將ai先按大小排序,再按下標排序(字典序,小的靠前)。處理每個k詢問,將前k大ai下標加入線段樹,求位置爲pos的元素。
代碼
int n, m, b[maxn], t[maxn<<2];
void add(int k, int l, int r, int pos)
{
if(pos < l || r < pos) return;
t[k]++;
if(l == r) return;
add(lson, l, mid, pos); add(rson, mid+1, r, pos); return;
}
int ask(int k, int l, int r, int y)
{
if(l == r) return l;
if(t[lson] >= y) return ask(lson, l, mid, y);
else return ask(rson, mid+1, r, y-t[lson]);
}
int cmp(Query aa, Query bb) { return aa.id < bb.id; }
int main()
{
n = read();
for(int i = 1; i <= n; i++) b[i] = a[i].v = read(), a[i].id = i;
sort(a+1, a+n+1);
m = read();
for(int i = 1; i <= m; i++) q[i].k = read(), q[i].pos = read(), q[i].id = i;
sort(q+1, q+m+1);
int tmp = 0;
for(int i = 1; i <= m; i++)
{
while(q[i].k > tmp) add(1, 1, n, a[++tmp].id);
q[i].ans = ask(1, 1, n, q[i].pos);
}
sort(q+1, q+m+1, cmp);
for(int i = 1; i <= m; i++) printf("%d\n", b[q[i].ans]);
return 0;
}
B. Box (1s 256Mb)
題目大意
給出 n(1e5),n個qi,表示一個排列前i個元素裏的最大值,求構造任意一個合法排列,沒有輸出-1。
分析
如果qi>qi+1,那麼pi+1 = qi+1;否則,使用盡量小的未使用元素去填,沒有可填的則非法。
代碼
int main()
{
int T = read();
while(T--)
{
n = read();
int mx = 0, pos = 1, flag = 1;
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++)
{
if(a[i] < mx)
{
flag = 0;
break;
}
else if(a[i] > mx)
{
if(vis[a[i]]) { flag = 0; break; }
mx = ans[i] = a[i];
vis[a[i]] = 1;
}
else
{
while(pos <= n && vis[pos]) pos++;
if(pos > mx) { flag = 0; break; }
vis[ans[i] = pos] = 1;
}
}
if(!flag) printf("-1\n");
else
{
for(int i = 1; i < n; i++) printf("%d ", ans[i]);
printf("%d\n", ans[n]);
}
memset(vis, 0, sizeof(int)*(n+3));
}
return 0;
}