细节。。。决定成败
T2数组开小,T3long long没开够。
而且其实不止这样,考试结束前15分钟发现了好多低错:
T3双向边没开2倍。dfs没递归调用。T2为了调试bitset开20没改(后来改成了6000,虽说还是错的但是还是好了不少)
一定要手模几个样例测一下。严格注意数组大小
最后几天了,一定要注意这种细节了。
T1:Adore
状压,dp。
复杂度$O(mk imes 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 ",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 ",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 ",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 ",ans); 26 }
自家OJ数据水了,去BZOJ1117自测吧。(送个链接)