這是什麼
午休,大黃突然走進來
大黃:閃電特效!
其他人:?
大黃:5k!
其他人:???
大黃:
【閃電特效】【閃電特效】
男人中的男人
【閃電特效】【閃電特效】
雄性中的雄性
【閃電特效】【閃電特效】
巔峰!
【閃電特效】【閃電特效】
A.好數
簡單變形一下
然後 \(f_j+f_k\) 是可以 \(n^2\) 維護的,所以你直接把這玩意丟桶裡
直接在桶裡查 \(c-f_i\) 就行了
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[5001];
int cnt2[1000001];
const int dx=500000;
int ans,ans2;
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;++i){
for(int j=1;j<=i-1;++j){
if(cnt2[a[i]-a[j]+dx]){
ans2++;
break;
}
}
for(int k=1;k<=i;++k){
cnt2[a[i]+a[k]+dx]=true;
}
}
cout<<ans2;
}
B.SOS 字串
賽時打的記搜不夠優,所以沒怎麼拿分
我的思路是記一下字串裡有多少萬用字元,最後快速冪一塊乘起來,賽後發現自己就像若智一樣,這麼寫既浪費時間又拖慢記憶化
所以,設 \(f_{i,0/1/2,k}\) 表示當前考慮到第 \(i\) 位,在最近的一個 SOS
中未填(\(0\)),填到第一個 S
(\(1\)),填到 O
,並且已經填完了 \(k\) 個 SOS
的方案數,直接按這個思路轉移即可
注意點
- 這題卡掉了 map 的記
- \(k\ge 3\) 的情況本質是一樣的,所以不要再去考慮 \(k\gt 3\) 的情況了,轉移的時候直接對 \(3\) 取 \(\min\)
分討都在註釋裡了
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=1e9+7;
int n;
int ans[]={
0,
//0
0,0,0,0,0,0,0,0,1,104,6757,351205,15973496,664272712,899139165,727408931,230839283,110517859,10938323,18380962,801591834,538331878,360540447,210225942,551547105,929157669,272821968,705926995,775855957,92872414,973270409,391860935,335059301,85671406,897860651,44139736,589503918,625697165,907940909,65840028,296877522,118994829,791306555,545227129,339582817,556088554,498324683,790874686,448130225,415804878,265383931,350869320,508306396,230852520,207518131,393727895,10918291,217898849,356629754,725800340,721470986,818807886,418275274,98166057,166836105,700350963,284803896,926512861,894412347,395302972,592929019,126921505,647951981,929483993,943743437,633413084,615394211,839148006,505351030,635923265,581764455,628567567,602039917,894182098,22633948,459519338,392370752,266609453,230295067,470625000,553777871,371352394,924404107,267364811,494994623,656969894,411339471,219584748,278697715,
//1-99
386050963,938717640,475120605,760806437,753435202,314937563,851110504,797356893,245785631,262365753,833638573,356358070,487921967,794091475,326352338,473729558,565259085,171901369,335540820,814721033,161257365,217687425,155138438,808186780,386518022,83983404,717178046,78856463,235246882,499975775,733374510,215560962,110112138,900818712,197816052,679749741,695794560,543244371,289768441,371534780,751829883,510022775,825226346,407146882,671249767,392207831,889684111,972461673,42143586,173024277,930675066,714500895,313370867,972865022,223928126,923599447,178339933,889909549,40600138,975842829,849125694,668460497,952095956,598470711,655030486,322257610,772694338,624583367,4058244,987656118,585739412,576086511,325923294,813523092,122889315,110009872,706599969,267933649,400120157,886092645,943382227,712297353,218809367,496788446,84125031,637155241,142014530,438296963,902423764,422839788,724584421,920976540,92270597,170789534,937908052,738236019,394787814,710269016,541613082,88290832,325997693
//100-200
};
inline int power(int a,int t){
int base=a,ans=1;
while(t){
if(t&1){
ans=ans*base%p;
}
base=base*base%p;
t>>=1;
}
return ans;
}
int f[1000001][4][5];
int dfs(int now,int sta,int soscnt){
if(now>n){
return (soscnt>=3)%p;
}
if(f[now][sta][soscnt]){
return f[now][sta][soscnt];
}
int res=0;
if(sta==2){
res=(res+dfs(now+1,0,soscnt))%p; //O
res=(res+dfs(now+1,0,soscnt==3?3:soscnt+1))%p; //S
res=(res+24*dfs(now+1,0,soscnt))%p; //*
}
if(sta==1){
res=(res+dfs(now+1,2,soscnt))%p; //O
res=(res+dfs(now+1,1,soscnt))%p; //S
res=(res+24*dfs(now+1,0,soscnt))%p; //*
}
if(sta==0){
res=(res+dfs(now+1,0,soscnt))%p; //O
res=(res+dfs(now+1,1,soscnt))%p; //S
res=(res+24*dfs(now+1,0,soscnt))%p; //*
}
return f[now][sta][soscnt]=res%p;
}
signed main(){
freopen("sos.in","r",stdin);
freopen("sos.out","w",stdout);
cin>>n;
cout<<dfs(1,0,0);
}
P4141.消失之物
直接做 \(q\) 遍 DP 的話,有很多都是重複計算的
考慮怎麼才能只做一遍 DP
我們在做 DP 的時候,如果選了 \(i\),轉移為
f[j]+=f[j-w[i]]
所以當我們不選 \(i\) 的時候,從答案裡減掉的貢獻即為
f[j]-=f[j-w[i]]
乍一看不是很對,實際上直接無腦減確實是不對的,但是隻要滿足轉移和撤銷是反著來的,那麼,後更新的就會先被撤銷,那麼就一定是正確的了(當然,如果你開兩維就可以很好地避免動這個腦子)
為什麼要在最終狀態上撤?其實在這裡撤和在轉移到 \(i\) 撤是一樣的,雖然答案增加了,但是兩個版本的答案的差並沒變
#include<bits/stdc++.h>
using namespace std;
const int p=10000;
int n,m;
int w[2001];
int f[2001];
int g[2001];
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&w[i]);
}
memset(f,0,sizeof f);
f[0]=1;
for(int i=1;i<=n;++i){
for(int j=m;j>=0;--j){
if(j>=w[i]) f[j]=(f[j]+f[j-w[i]])%p;
}
}
for(int i=1;i<=n;++i){
memcpy(g,f,sizeof f);
for(int j=w[i];j<=m;++j){
g[j]=(g[j]-g[j-w[i]]+p)%p;
}
for(int j=1;j<=m;++j){
cout<<g[j]%10;
}
cout<<'\n';
}
}
二維寫法
#include<bits/stdc++.h>
using namespace std;
const int p=10000;
int n,m;
int w[2001];
int f[2001][2001];
int g[2001];
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&w[i]);
}
memset(f,0,sizeof f);
f[0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<=m;++j){
if(j>=w[i]) f[i][j]=(f[i][j]+f[i-1][j-w[i]])%p;
f[i][j]=(f[i][j]+f[i-1][j])%p;
}
}
for(int i=1;i<=n;++i){
memcpy(g,f[n],sizeof f[n]);
for(int j=w[i];j<=m;++j){
g[j]=(g[j]-g[j-w[i]]+p)%p;
}
for(int i=1;i<=m;++i){
cout<<g[i]%10;
}
putchar('\n');
}
}
C.集訓營的氣球
和上一個題類似,這個題無非就是加一個重新 DP 的過程
設 \(f_i\) 表示有 \(i\) 個人選了強制選的那個,正難則反,我們可以求出不合法的方案書,再用總方案去減
總方案很好求,\(tot=\prod\limits(a_i+b_i)\),剩下的不合法方案數直接套模板
總方案數的修改 \(tot'=\frac{tot'}{a_i\times b_i}\times(a_i'\times b_i')\)
揹包的轉移
(這個轉移就是考慮到當前人,他選擇 \([1,a_i]\) 內的任意一個就會貢獻一個強制選,否則不會)
撤銷
無非就是除以原來的方案數,乘上現在的方案數
除以原來的方案數
乘上現在的方案數,和前面轉移是一樣的,這就是撤銷後的恢復操作
要注意的是 \(f_0\) 作為起始值需要單獨轉移
除法直接用逆元處理即可
(\(p\) 為修改位置)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=1e9+7;
int power(int a,int t){
int base=a,ans=1;
while(t){
if(t&1){
ans=ans*base%p;
}
base=base*base%p;
t>>=1;
}
return ans;
}
int a[1000001],b[1000001];
/* not satisfied the request
f_i record the number that has f_i people choose balloon
ans = tot - sum{f_i} */
int f[21];
int n,c,q;
int tot;
signed main(){
freopen("balloon.in","r",stdin);
freopen("balloon.out","w",stdout);
scanf("%lld %lld",&n,&c);
for(int i=1;i<=n;++i){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;++i){
scanf("%lld",&b[i]);
}
tot=f[0]=1;
// do dp at first status
for(int i=1;i<=n;++i){
for(int j=c-1;j>0;--j){
f[j]=(f[j-1]*a[i]+f[j]*b[i])%p;
/* f the ith people don't choose,
then there's f_j to f_j,
otherwise f_{j-1} to f_j*/
}
f[0]=(f[0]*b[i])%p;
/*
still nobody choose
*/
tot=(tot*(a[i]+b[i]))%p;
}
scanf("%lld",&q);
while(q--){
int id,x,y;
scanf("%lld %lld %lld",&id,&x,&y);
tot=tot*power(a[id]+b[id],p-2)%p*(x+y)%p;
/*cal the new tot*/
int inv=power(b[id],p-2);
f[0]=f[0]*inv%p;
/*cal the new f_0*/
for(int i=1;i<=c-1;++i){
f[i]=(f[i]-f[i-1]*a[id]%p+p)*inv%p;
}
/*undo the dp*/
for(int i=c-1;i>0;--i){
f[i]=(f[i-1]*x+f[i]*y)%p;
}
/*redo the dp*/
f[0]=f[0]*y%p;
a[id]=x;b[id]=y;
int ans=tot;
for(int i=0;i<=c-1;++i){
ans=(ans-f[i]+p)%p;
}
cout<<ans<<'\n';
}
}
推歌
ギャンビット 雄之助feat.初音未來
ショーケース
固定された姿のままで
悲しむマネキンな自分の
提唱する感傷など
投げ捨てろ
還元なき逃走には
リスクが付き物
結局はあいつらが
嫌いなだけなのさ
関係ない人も
敵に見えてたんだ
Fighting back 依存気味の
借り木を取り除いて
橫やりばかりな
妄執へと言い聞かせる
Dancing Doll 終わるのには
早いと気付けたなら
踴り続ける
その足で蹴り飛ばせ
力の限り
Face is handmade
このハッタリ堪えがたい
Nightmare maze
性別や姿がどうしたんだ
苦しむPrideたちがEnemyは
ほんの一部だけだと指を差す
結局は知って欲しい
火打ちを奪われて
消えかけの意志で
當たり散らした
本當は何回も
思ったことがある
取り返す勇気と
自信さえあればとずっと
乾坤一擲
推して參れ
Free Phase
Don't come back here
Don't come back here
逆を向いた月が
夜闇に呑まれようと
鋭く尖らせて
Gambit 犠牲の上
目覚めた今があれば
あらゆる過去など
餌にしてもお釣りがくる
Fighting back 塗り潰した
切り目を引き直せ
感情は渡さない
あいつらに渡さない
這是什麼
huge on the front
i can't view a beautiful laopo