細節。。。決定成敗
T2數組開小,T3long long沒開夠。
而且其實不止這樣,考試結束前15分鍾發現了好多低錯:
T3雙向邊沒開2倍。dfs沒遞歸調用。T2為了調試bitset開20沒改(后來改成了6000,雖說還是錯的但是還是好了不少)
一定要手模幾個樣例測一下。嚴格注意數組大小
最后幾天了,一定要注意這種細節了。
T1:Adore
狀壓,dp。
復雜度$O(mk \times 2^k)$不夠優但是足以通過。

1 #include<cstdio> 2 int add(int &a,int b){a+=b;if(a>=998244353)a-=998244353;} 3 int cntbit[1025],m,k,dp[2][1024],E[11],NE[11],ans; 4 int re(){register char ch=getchar(); 5 while(ch<'0'||ch>'1')ch=getchar(); 6 return ch-'0'; 7 } 8 int main(){ 9 freopen("adore.in","r",stdin);freopen("adore.out","w",stdout); 10 for(int i=1;i<1024;++i)cntbit[i]=cntbit[i^i&-i]+1; 11 scanf("%d%d",&m,&k);m-=3; 12 int st=0,ths=0,nxt=1; 13 for(int i=0;i<k;++i)st|=re()<<i; 14 dp[nxt][st]=1; 15 while(m--){ 16 ths^=1;nxt^=1; 17 for(int i=0;i<1<<k;++i)dp[nxt][i]=0; 18 for(int i=0;i<k;++i)E[i]=NE[i]=0; 19 for(int i=0;i<k;++i)for(int j=0,x;j<k;++j)x=re(),E[i]|=x<<j,NE[j]|=x<<i; 20 for(int s=0;s<1<<k;++s)if(dp[ths][s]){ 21 int tst=0,ntst=0; 22 for(int i=0;i<k;++i)if(s&1<<i)tst^=E[i],ntst^=NE[i]; 23 add(dp[nxt][tst],dp[ths][s]);add(dp[nxt][ntst],dp[ths][s]); 24 } 25 }st=0; 26 for(int i=0;i<k;++i)st|=re()<<i; 27 for(int i=0;i<1<<k;++i)if(!(cntbit[st&i]&1))add(ans,dp[nxt][i]); 28 printf("%d\n",ans); 29 }
T2:Confess
手動構造,發現交集大於n的很多。所以采用隨機化。注意數組大小。

1 #include<bits/stdc++.h> 2 using namespace std; 3 bitset<12005>B[6005]; 4 int n,k;char s[6005]; 5 int main(){ 6 freopen("confess.in","r",stdin);freopen("confess.out","w",stdout); 7 scanf("%d%s",&n,s); 8 while(s[k])k++; 9 int cnt=0; 10 for(int i=0;i<k;++i){ 11 int x=s[i]-33; 12 for(int j=0;j<6&&cnt<=n<<1;++j)B[1][cnt]=(x&1<<j?1:0),cnt++; 13 } 14 for(int I=2;I<=n+1;++I){ 15 scanf("%s",s); 16 int cnt=0; 17 for(int i=0;i<k;++i){ 18 int x=s[i]-33; 19 for(int j=0;j<6&&cnt<=n<<1;++j)B[I][cnt]=(x&1<<j?1:0),cnt++; 20 } 21 } 22 srand(time(0)); 23 while(1){ 24 int a=rand()%(n+1)+1,b=rand()%(n+1)+1; 25 while(a==b)b=rand()%(n+1)+1; 26 if((B[a]&B[b]).count()>=n>>1)return printf("%d %d\n",a,b),0; 27 } 28 }
T3:Repulsed
設dp[i][j]表示距離i這個點j條邊的需要滅火器的子節點有多少個。Idp[i][j]表示距離i點有j條邊的還沒用完的滅火器還能用幾次。
在一棵子樹內,互相消除,然后上傳。
如果有的點dp[i][k]>0而Idp[i][0]=0那么就需要申請新的滅火器。
從遠到近依次解決需求,不斷上傳。最后在1號節點特殊處理:不管剩下多少需求都要直接申請滅火器解決。
注意Idp數組需要開longlong。

1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,k,s,fir[100005],l[200005],to[200005],ec,ans,dp[100005][21];long long Idp[100005][21]; 4 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 5 void dfs(int p,int fa){ 6 dp[p][0]++; 7 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 8 dfs(to[i],p); 9 for(int j=0;j<k;++j)dp[p][j+1]+=dp[to[i]][j],Idp[p][j+1]+=Idp[to[i]][j]; 10 } 11 while(dp[p][k]>Idp[p][0])Idp[p][0]+=s,ans++; 12 for(int i=k;~i;--i)for(int j=k-i;~j;--j){ 13 int x=min(1ll*dp[p][i],Idp[p][j]); 14 dp[p][i]-=x;Idp[p][j]-=x; 15 } 16 if(p==1){ 17 int totcnt=0; 18 for(int i=0;i<=k;++i)totcnt+=dp[p][i]; 19 while(totcnt>0)totcnt-=s,ans++; 20 } 21 } 22 int main(){ 23 freopen("repulsed.in","r",stdin);freopen("repulsed.out","w",stdout); 24 scanf("%d%d%d",&n,&s,&k);if(s>n)s=n; 25 for(int i=1,a,b;i<n;++i)scanf("%d%d",&a,&b),link(a,b),link(b,a); 26 dfs(1,0);printf("%d\n",ans); 27 }
上述算法稍偽。當且僅當需求距離和滅火器距離加和為k或k-1時才會配對,否則就可以上傳,以后再匹配。
上傳答案一定不會變差,反而可能找到更優的匹配。
要注意根節點就可以隨意匹配了。
代碼基本沒有變。同時時間復雜度也下降到了$O(nk)$

1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,k,s,fir[100005],l[200005],to[200005],ec,ans,dp[100005][21];long long Idp[100005][21]; 4 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 5 void dfs(int p,int fa){ 6 dp[p][0]++; 7 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 8 dfs(to[i],p); 9 for(int j=0;j<k;++j)dp[p][j+1]+=dp[to[i]][j],Idp[p][j+1]+=Idp[to[i]][j]; 10 } 11 while(dp[p][k]>Idp[p][0])Idp[p][0]+=s,ans++; 12 for(int i=k;i>=((p==1)?0:k-1);--i)for(int j=i;~j;--j){ 13 int x=min(1ll*dp[p][j],Idp[p][i-j]); 14 dp[p][j]-=x;Idp[p][i-j]-=x; 15 } 16 if(p==1){ 17 int totcnt=0; 18 for(int i=0;i<=k;++i)totcnt+=dp[p][i]; 19 while(totcnt>0)totcnt-=s,ans++; 20 } 21 } 22 int main(){ 23 scanf("%d%d%d",&n,&s,&k); 24 for(int i=1,a,b;i<n;++i)scanf("%d%d",&a,&b),link(a,b),link(b,a); 25 dfs(1,0);printf("%d\n",ans); 26 }
自家OJ數據水了,去BZOJ1117自測吧。(送個鏈接)