Noip模拟19(炸裂的开始) 2021.7.18
阅读原文时间:2023年07月08日阅读:7

T1 u

差分与前缀的综合练习。

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

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

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

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

思路挺神的

知识点

差分与前缀

标记下放

1 #include
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();}

T2 v

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

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

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

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

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

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

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

大概就是:

1 将数列向右移动几位

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

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

知识点:

记忆化搜索

状压dp

哈希表

1 #include
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<-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-j+1<>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();}

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同理表示偶数的

1 #include
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();}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章