Noip模拟19(炸裂的开始) 2021.7.18

T1 u

差分与前缀的综合练习。

分析数据范围,只能是在修改的时候$O(1)$做到,那么只能是像打标记一样处理那个三角形

正解是建立两个二位前缀和,一个控制竖向,一个控制斜向

每次在三角的左上,右下,左下几个位置分别打上加一或者减一的标记

之后$N^2$查询时直接将标记“下放”就可以求出正确的异或和

思路挺神的

知识点

差分与前缀

标记下放

Noip模拟19(炸裂的开始) 2021.7.18
 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 inline int read(){
 5     int x=0,f=1; char ch=getchar();
 6     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 7     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
 8     return x*f;
 9 }
10 const int NN=4005;
11 int n,q;
12 int d1[NN][NN],d2[NN][NN];
13 namespace WSN{
14     inline int main(){
15         n=read(); q=read();
16         while(q--){
17             int r=read(),c=read(),l=read(),s=read();
18             d1[r][c]+=s;
19             d1[r+l][c]-=s;
20             d2[r][c+1]-=s;
21             d2[r+l][c+l+1]+=s;
22         }
23         int ans=0,tmp=0;
24         for(int i=1;i<=n;i++){
25             tmp=0;
26             for(int j=1;j<=n;j++){
27                 tmp+=d1[i][j]+d2[i][j];
28                 d1[i+1][j]+=d1[i][j];
29                 d2[i+1][j+1]+=d2[i][j];
30                 ans^=tmp;
31             }
32         }
33         printf("%lld\n",ans);
34         return 0;
35     }
36 }
37 signed main(){return WSN::main();}
View Code

T2 v

一看数据范围,就是知道是个状压,但是根本不懂如何定义数组。。

更何况还有一个后效性的问题。。。

但是如果足够暴力的将每一种可能的删球情况都枚举出来的话,就没事了

那么肯定会T,考虑记忆化搜索

我们设$m[sta]$表示在$sta$状态下的期望值

因为在进行$dfs$时状态会重复,考虑使用哈希表去重

然后就是在进行状压时候的状态转移,比较神

大概就是:

1 将数列向右移动几位

2 将数列往回移动那么多位-1,相当于去掉了一位

3 将原来数列里面的后几位提取过来,给那个位移了的数列拼起来

知识点:

记忆化搜索

状压dp

哈希表

Noip模拟19(炸裂的开始) 2021.7.18
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,k;char ch[35];
 4 struct HASH{
 5     struct edge{int to,next;};edge e[50000013];int head[50000013],tot; short leng[50000013],len; double val[50000013];
 6     double &operator[](int sta){
 7         int x=sta*len%50000013;
 8         for(int i=head[x];i;i=e[i].next) if(e[i].to==sta&&leng[i]==len) return val[i];
 9         e[++tot]=(edge){sta,head[x]}; leng[tot]=len; head[x]=tot; return val[tot]=-1.0;
10     }
11 }m;
12 inline double dp(int len,int sta){
13     // cout<<len<<endl;
14     if(len==n-k) return 0.0;
15     m.len=len; if(m[sta]>-0.5) return m[sta];//在返回记忆化值之前记录长度,否则会出现后效性
16     m[sta]=0;//记得定义初始状态值,否则样例返回-0.0(因为原本不定义初始值m[sta]为-1,减一后正好为-0.0)
17     int pos[35],st=sta;
18     for(int i=1;i<=len;i++) pos[i]=st&1,st>>=1;//复制原状态到数组备用(是反着赋值)
19     for(int i=1;i<=len/2;i++){
20         int st1=sta>>len-i+1<<len-i|sta&(1<<len-i)-1;//从左面删除
21         int j=len-i+1;
22         int st2=sta>>len-j+1<<len-j|sta&(1<<len-j)-1;//从右面删除
23         double ans1=dp(len-1,st1)+pos[j];
24         double ans2=dp(len-1,st2)+pos[i];
25         m.len=len; m[sta]+=2.0/len*max(ans1,ans2);
26     }
27     if(len&1){//如果是奇数就判断中间之前没有循环到的一位
28         int mid=len/2+1;
29         int st3=sta>>len-mid+1<<len-mid|sta&(1<<len-mid)-1;
30         double ans3=dp(len-1,st3)+pos[mid];
31         m.len=len; m[sta]+=1.0/len*ans3;
32     }
33     return m[sta];
34 }
35 namespace WSN{
36     inline int main(){
37         scanf("%d%d%s",&n,&k,ch+1); int sta=0;
38         for(int i=1;i<=n;i++) sta=sta<<1|(ch[i]=='W');
39         printf("%.10lf",dp(n,sta));
40         return 0;
41     }
42 }
43 signed main(){return WSN::main();}
View Code

T3 w

第一眼,可恶,又是$dp$。。

树形$dp$当时学的比较糟糕(不过假期讲课skyh大神讲过后明白了许多)

这题有两个难点

一个是关于状态的定义,

我们发现可以把c,d$xor$表示这个边是否需要反转

考虑最小操作次数,其实就是度数为奇数的点数/2,这里的度数指的是所连的最终状态与之前不一样的边数,包括必须翻和不必须。

那么我们开一个结构体记录度数为奇数的点数和链的长度,结构题数组$dp_{i,0/1}$表示$i$上面的一条边反转与否,并记录儿子的信息

第二个是分类讨论,

如果度数为奇,

度数为奇的儿子不用翻,之前是偶数儿子用翻取min

如果度数为偶,

度数为偶的儿子用翻,之前是偶数儿子不用翻取min

统计完儿子的信息就要把自己加进子树中。

如果当前点和父亲必须翻,为自己是奇数的个数,长度+1和自己是偶数的个数+1,长度+1取min。

如果当前点和父亲必须不翻,为自己是奇数的个数+1,长度和自己是偶数的个数,长度。

如果可翻可不翻,那么两种状态都会存在。

转移的时候使用中间量$w_1,w_2,n_1,n_2$1表示奇度数的转移信息,2同理表示偶数的

Noip模拟19(炸裂的开始) 2021.7.18
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int NN=1e5+5,inf=0x3f3f3f3f;
 4 int n;
 5 struct SNOW{int to,next,c;};SNOW e[NN<<1]; int head[NN],rp;
 6 struct DP{
 7     int sum,len;
 8     DP(){}
 9     DP(int x,int y){this->sum=x; this->len=y;}
10     DP operator+(DP x) const{
11         DP ans;
12         ans.len=len+x.len;
13         ans.sum=sum+x.sum;
14         return ans;
15     }
16     bool operator<(DP x) const{
17         return sum==x.sum? len<x.len : sum<x.sum;
18     }
19 }dp[NN][3];
20 inline void add(int x,int y,int z,int w){
21     e[++rp].to=y; e[rp].next=head[x]; e[rp].c=(w==2?2:z^w); head[x]=rp;
22     e[++rp].to=x; e[rp].next=head[y]; e[rp].c=(w==2?2:z^w); head[y]=rp;
23 }
24 inline void dfs(int f,int x,int opt){
25     DP w1=DP(inf,inf);
26     DP w2=DP(0,0);
27     for(int i=head[x];i;i=e[i].next){
28         int y=e[i].to;
29         if(y==f) continue;
30         dfs(x,y,e[i].c);
31         DP n1=min(w2+dp[y][1],w1+dp[y][0]);
32         DP n2=min(w1+dp[y][1],w2+dp[y][0]);
33         w1=n1; w2=n2;
34     }
35     if(opt==0){
36         dp[x][1]=DP(inf,inf);
37         dp[x][0]=min(DP(w1.sum+1,w1.len),w2);
38     }
39     else if(opt==1){
40         dp[x][1]=min(DP(w2.sum+1,w2.len+1),DP(w1.sum,w1.len+1));
41         dp[x][0]=DP(inf,inf);
42     }
43     else{
44         dp[x][1]=min(DP(w2.sum+1,w2.len+1),DP(w1.sum,w1.len+1));
45         dp[x][0]=min(DP(w1.sum+1,w1.len),w2);
46     }
47 }
48 namespace WSN{
49     inline int main(){
50         scanf("%d",&n);
51         for(int i=1,a,b,c,d;i<n;i++) scanf("%d%d%d%d",&a,&b,&c,&d),add(a,b,c,d);
52         dfs(0,1,0); printf("%d %d\n",dp[1][0].sum/2,dp[1][0].len);
53         return 0;
54     }
55 }
56 signed main(){return WSN::main();}
View Code

 

上一篇:Wifi Null Data帧


下一篇:Solution -「NOI 2021」「洛谷 P7740」机器人游戏