CSP-S 模拟53 题解

题解:

T1 u:

一看到修改这么多,但询问其实只有一个不难想到差分,但是他这个形状可以说很不规则,于是我们想到分别维护竖着的和斜着的差分,然后最后合并即可。

考场上瞎调了一波系数莫名AC,其实是维护差分的差分。

考试时发现对拍暴力输不出来东西时,慌的不行,对拍的数据范围一定要搞对。

 1 //weihu xiezhede chafen?
 2 //对于每个满足 x ∈ [r, r +l), y ∈ [c, x−r +c]
 3 //的元素 (x, y),将权值增加 s。
 4 #include<bits/stdc++.h>
 5 using namespace std;
 6 #define int long long
 7 #define sc(x) printf("%lld\n",x)
 8 const int N=1e3+10;
 9 int cf[N][N],cf1[N][N],n,q,a[N][N];
10 signed main(){
11     //freopen("data.in","r",stdin);
12     //freopen("my.out","w",stdout);
13     scanf("%lld%lld",&n,&q);
14     if(!q){puts("0");return 0;}
15     for(int i=1;i<=q;++i){
16         int r,c,l,s;
17         scanf("%lld%lld%lld%lld",&r,&c,&l,&s);
18         cf[r][c]+=s;
19         cf1[r][c+1]+=s;
20         if(r+l<=n) cf[r+l][c]-=s;
21         if(r+l<=n&&c+l+1<=n) cf1[r+l][c+l+1]-=s;
22     }
23     for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) cf[i][j]+=cf[i-1][j];
24     for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) cf1[i][j]+=cf1[i-1][j-1];
25     int ans=0;
26     for(int i=1;i<=n;++i){
27         for(int j=1;j<=n;++j){
28             cf[i][j]-=cf1[i][j];
29         }
30     }
31 //    ans=cf[1][1];
32     for(int i=1;i<=n;++i){
33         for(int j=1;j<=n;++j){
34             cf[i][j]+=cf[i][j-1];
35             //cout<<cf[i][j]<<" ";
36             ans^=cf[i][j];
37         }
38         //cout<<endl;
39     }
40     sc(ans);
41 }
u

 

T2 v:

考场上想状压,但是发现数据范围稍大,可能过不了,然后也没什么思路。

正解 记忆化搜索+hash_map,其实是和裸状压一样的,但是加了记忆化加速,比较难搞的一点就是如何把删了一个球后的状态用二进制表示出来,有点绕。

还有就是,hash_map不能仅仅记录状态,还要记录当前所剩的球数,因为000110和000110对于状态来说是一样的,但是前面的黑球数是不一定的,这样保证状态唯一。所以hash_map有点难搞,%%%DeepinC重载中括号取地址hash_map,因为自己是实在是不会hash_map,所以就没脸得照着DeepinC神的hash_map打了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=35;
 4 #define signed long long
 5 const int M=3e7+10;
 6 char s[N];
 7 int n,k,a[N];
 8 struct hash_map{//hash_map shizhaozhe DeepinC dedade zhendebuhui hash_map
 9     int first[30000017],nex[M],to[M],tot;short l[M],len;
10     double val[M];
11     double &operator[] (const register int st){
12         signed f=st*1ll*len%30000017;
13         for(register int i=first[f];i;i=nex[i]) if(to[i]==st&&l[i]==len) return val[i];
14         to[++tot]=st,nex[tot]=first[f],first[f]=tot,l[tot]=len; val[tot]=-1;return val[tot];
15     }
16 }mp;
17 inline double max(const register double  a,const register double b){
18     return a>b?a:b;
19 }
20 
21 double dfs(const register int l,const register int st){//cout<<l<<" "<<st<<endl;
22     if(l==n-k) return 0.0;
23     mp.len=l;//meicichongxinfuzhi,yinwei zhuangtaihechangdushiduiyingde
24     if(mp[st]>-0.1) return mp[st];
25     mp[st]=0;
26     short sta[N],jk=l+1,kh=(l>>1)+1;
27     for(register int i=1;i^jk;++i) sta[i]=(st>>i-1)&1;
28 //    reverse(sta+1,sta+l+1);
29 //    for(int i=1;i<=l;++i) cout<<sta[i]<<" ";cout<<endl;
30 
31     for(register int  i=1;i^kh;++i){
32         short h=l-i+1;
33         int state1=st>>l-i+1<<l-i|st&(1<<l-i)-1,state2=st>>l-h+1<<l-h|st&(1<<l-h)-1;
34 //        cout<<st<<" "<<l<<" "<<i<<endl;
35 //        cout<<state1<<" "<<state2<<endl;
36         double ans1=dfs(l-1,state1)+sta[h],ans2=dfs(l-1,state2)+sta[i];
37         mp.len=l;mp[st]+=(2.0*max(ans1,ans2))/(1.0*l);
38     }
39     if(l&1){
40         int i=kh;
41         int state=st>>l-i+1<<l-i|st&(1<<l-i)-1;
42         double ans=dfs(l-1,state)+sta[i];
43         mp.len=l;mp[st]+=ans/(1.0*l);
44     }
45     return mp[st];
46 }
47 
48 int main(){
49     scanf("%d%d",&n,&k);
50     scanf("%s",s+1);
51     int st=0;
52     for(register signed i=1;i<=n;++i) a[i]=(s[i]=='W');
53     for(register signed i=1;i<=n;++i) st=st<<1|a[i];
54     printf("%.7lf",dfs(n,st));
55 }
v

T3:

考场上以为是个贪心,就和虎那题差不多,实际上是个思路非常好的dp

首先贪心地考虑,每个边顶多被覆盖一次,因为被覆盖两次以上完全可以从这条便断开,那么会使答案更优。

我们设$dp[i][0/1]$表示以点i为根的子树否/是向上伸,因为他的两个答案是同时更新的,所以。

然后考虑对于i的儿子y,我们设两个二元组w1,w2,w1表示向上伸,w2表示不向上伸。

那么我们考虑转移$w1=min(w1+dp[y][0],w2+dp[y][1])$,$w2=min(w1+dp[y][1],w2+dp[y][0])$

那么我们在来考虑怎么用w1,w2进行转移。

如果他这条边不翻转,那么能不反转就不反转,所以$dp[x][1]=(INF,INF)$,$dp[x][0]=min(w2,(w1.first+1,w1.second))$

如果必须反转的话,那么同理$dp[x][0]=(INF,INF)$,$dp[x][1]=min((w1.first,w1.second+1),(w2.first+1,w2.second+1))$

最后答案就是$dp[1][0].first/2$和$dp[1][0].second$

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=3e5+10,INF=1e9+10;
 4 int first[N],nex[N<<1],to[N<<1],edge[N<<1],tot;
 5 void add(int a,int b,int c){
 6     to[++tot]=b,nex[tot]=first[a],first[a]=tot,edge[tot]=c;
 7 }
 8 pair<int ,int> min(pair<int ,int> a,pair<int ,int > b){
 9     return a<b?a:b;
10 }
11 pair<int ,int> add(pair<int,int> a,pair<int ,int> b){
12     return make_pair(a.first+b.first,a.second+b.second);
13 }
14 pair<int ,int> dp[N][2];//0 not up 1 up
15 void dfs(int x,int fa,int ed){
16     pair<int ,int > p,q;
17     p=make_pair(INF,INF);//up
18     q=make_pair(0,0);//not up
19     pair<int ,int> tmp1,tmp2;//p q;
20     for(int i=first[x];i;i=nex[i]){
21         int y=to[i];
22         if(y==fa) continue;
23         dfs(y,x,edge[i]);
24         tmp1=min(add(p,dp[y][0]),add(q,dp[y][1]));
25         tmp2=min(add(q,dp[y][0]),add(p,dp[y][1]));
26         p=make_pair(tmp1.first,tmp1.second);
27         q=make_pair(tmp2.first,tmp2.second);
28     }
29     if(ed==1||ed==2){//bi fan
30         dp[x][1]=min(make_pair(p.first,p.second+1),make_pair(q.first+1,q.second+1));
31     }else dp[x][1]=make_pair(INF,INF);
32     if(ed==0||ed==2){//bu fan
33         dp[x][0]=min(q,make_pair(p.first+1,p.second));
34     }else dp[x][0]=make_pair(INF,INF);
35 }
36 
37 int main(){
38     int n;
39     scanf("%d",&n);
40     for(int i=1;i<n;++i){
41         int a,b,c,d;
42         scanf("%d%d%d%d",&a,&b,&c,&d);
43         int opt;
44         if(d==2) opt=2;
45         else if(c^d) opt=1;
46         else opt=0;
47         add(a,b,opt);
48         add(b,a,opt);
49     }
50     dfs(1,0,2);
51     printf("%d %d",dp[1][0].first>>1,dp[1][0].second);
52 }
View Code

 

posted @ 2019-09-28 17:48  Barça_10  阅读(199)  评论(0编辑  收藏  举报