Loading

Noip模拟11 2021.7.11

T1 math

其实看看题面,看看给的那机组数据即可看出规律了(然而当时并没有,只是发现模数的循环节,存了个vector,接下来就暴力了)

有个柿子: 其实就是裴蜀定理。

然后想一想的话就是

 

 那么只要求出Ν个的gcd再和k求gcd,算出来之后用这个总的gcd去进行翻倍

反到最大的小于k的数停止即可

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 #define write(X) printf("%lld\n",X)
 4 #define Min(A,B) ((A)<(B)?(A):(B))
 5 using namespace std;
 6 inline int read(){
 7     int x=0,f=1; char ch=getchar();
 8     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 9     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
10     return x*f;
11 }
12 const int NN=5e6;
13 int n,k,GCD,ans[NN],tot;
14 inline int gcd(int a,int b){
15     if(b==0) return a;
16     return gcd(b,a%b);
17 }
18 namespace WSN{
19     inline int main(){
20         n=read(),k=read();
21         GCD=gcd(read(),k);
22         for(int i=1;i<n;i++){ int a=read();GCD=gcd(GCD,a);}
23         // cout<<GCD<<endl;
24         for(int i=0;i<=k;i++){
25             if(GCD*i>=k) break;
26             ans[++tot]=GCD*i;
27         }
28         write(tot);
29         for(int i=1;i<=tot;i++) printf("%lld ",ans[i]);
30         return 0;
31     }
32 }
33 signed main(){return WSN::main();}
34 // FILE *A=freopen(".in","r",stdin);
35 // FILE *B=freopen("aaa.out","w",stdout);
View Code

T2 biology

这题当时一看是个dp就先去看T3了,T3水了个线段树回来干打了一个正确性颇高的贪心

正解的初始柿子还是很好想的,不过对于dp弱者来说循环就很麻烦。。。

可是,这题的循环也并不麻烦。。。

首先柿子:

 

 然后看上届大佬的题解里说道:

dp方程里面的绝对值要拆开!!!

然后可以得到四种不同的情况:

将绝对值拆开,四个二维树状数组分别维护最大值:

 

可以画图,将一个方块分成四块,田字格中点为(i,j),则从左上,左下,右上,右下的四个区域v向此处转移,按照绝对值划分可分为:

 

 然后进行转移即可:

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 #define write(X) printf("%lld\n",X)
 4 #define Max(A,B) ((A)>(B)?(A):(B))
 5 using namespace std;
 6 inline int read(){
 7     int x=0,f=1; char ch=getchar();
 8     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 9     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
10     return x*f;
11 }
12 const int NN=2005,MM=2005*2005,NM=1e6+5;
13 int n,m,a[NN][NN],b[NN][NN],cnt;
14 int f[NN][NN];
15 vector< pair< int,int > > vc[MM];
16 bool vis[MM];
17 struct SNOW{
18     int t[NN][NN];
19     inline int lobit(int x){return x&(-x);}
20     inline void update(int x,int y,int val){
21         for(int i=x;i<=n;i+=lobit(x)) for(int j=y;j<=m;j+=lobit(y)) t[i][j]=Max(t[i][j],val);
22     }
23     inline int query(int x,int y){
24         int ans=0;
25         for(int i=x;i;i-=lobit(x)) for(int j=y;j;j-=lobit(y)) ans=Max(ans,t[i][j]);
26         return ans;
27     }
28 }t1,t2,t3,t4;
29 int na[MM],rk[MM];
30 inline bool cmp(int a,int b){return a<b;}
31 namespace WSN{
32     inline int main(){
33         n=read(),m=read();
34         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
35             a[i][j]=read();
36             if(!vis[a[i][j]] && a[i][j]) vis[a[i][j]]=1,na[++cnt]=a[i][j];
37         }
38         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) b[i][j]=read();
39         sort(na+1,na+cnt+1,cmp);
40         for(int i=1;i<=cnt;i++) rk[na[i]]=i;
41         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
42             if(a[i][j]) vc[rk[a[i][j]]].push_back(make_pair(i,j));
43         for(int i=0;i<vc[1].size();i++){
44             int x=vc[1][i].first,y=vc[1][i].second,vb=b[x][y];
45             t1.update(x,y,vb-x-y);t2.update(x,m-y+1,vb-x+y);t3.update(n-x+1,y,vb+x-y);t4.update(n-x+1,m-y+1,vb+x+y);
46         }
47         for(int i=2;i<=cnt;i++){
48             for(int j=0;j<vc[i].size();j++){
49                 int x=vc[i][j].first,y=vc[i][j].second,vb=b[x][y];
50                 f[x][y]+=Max(t1.query(x,y)+x+y+vb,Max(t2.query(x,m-y+1)+x-y+vb,Max(t3.query(n-x+1,y)-x+y+vb,t4.query(n-x+1,m-y+1)-x-y+vb)));
51             }
52             for(int j=0;j<vc[i].size();j++){
53                 int x=vc[i][j].first,y=vc[i][j].second,v=f[x][y];
54                 t1.update(x,y,v-x-y);t2.update(x,m-y+1,v-x+y);t3.update(n-x+1,y,v+x-y);t4.update(n-x+1,m-y+1,v+x+y);
55             }
56         }
57         int jie=0;
58         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) jie=Max(jie,f[i][j]);
59         write(jie);
60         return 0;
61     }
62 }
63 signed main(){return WSN::main();}
View Code

然而这只是四十分的打法....

正解其实不用考虑各种合法状态,因为最优状态是唯一的,绝对值对于你转移的方向是有限制的(即对于其他不合法状态,也就是绝对值出现负数的情况一定没有出现正数优)。那么先将节点转移为,便可以只记录四个变量,找出最大的一步合法状态即可。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 #define write(X) printf("%lld\n",X)
 4 #define Max(A,B) ((A)>(B)?(A):(B))
 5 using namespace std;
 6 inline int read(){
 7     int x=0,f=1; char ch=getchar();
 8     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 9     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
10     return x*f;
11 }
12 const int NN=2005,MM=2005*2005,NM=1e6+5;
13 int n,m,a[NN][NN],b[NN][NN],cnt;
14 int f[NN][NN];
15 vector< pair< int,int > > vc[MM];
16 bool vis[MM];
17 int t1,t2,t3,t4;
18 int na[MM],rk[MM];
19 inline bool cmp(int a,int b){return a<b;}
20 namespace WSN{
21     inline int main(){
22         n=read(),m=read();
23         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
24             a[i][j]=read();
25             if(!vis[a[i][j]] && a[i][j])
26                 vis[a[i][j]]=1,na[++cnt]=a[i][j];
27         }
28         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) b[i][j]=read();
29         sort(na+1,na+cnt+1,cmp);
30         for(int i=1;i<=cnt;i++) rk[na[i]]=i;
31         for(int i=1;i<=n;i++)
32             for(int j=1;j<=m;j++)
33                 if(a[i][j]) vc[rk[a[i][j]]].push_back(make_pair(i+j,i-j));
34         for(int i=0;i<vc[1].size();i++){
35             int x=vc[1][i].first,y=vc[1][i].second,opx=x+y>>1,opy=x-y>>1,vb=b[opx][opy];
36             t1=Max(t1,vb-x); t2=Max(t2,vb+x); t3=Max(t3,vb-y); t4=Max(t4,vb+y);
37         }
38         for(int i=2;i<=cnt;i++){
39             for(int j=0;j<vc[i].size();j++){
40                 int x=vc[i][j].first,y=vc[i][j].second,opx=x+y>>1,opy=x-y>>1,vb=b[opx][opy];
41                 int ans1=t1+vb+opx+opy,ans2=t2+vb-opx-opy,ans3=t3+vb+opx-opy,ans4=t4+vb-opx+opy;
42                 int ans=Max(ans1,Max(ans2,Max(ans3,ans4)));
43                 f[opx][opy]+=ans;
44             }
45             for(int j=0;j<vc[i].size();j++){
46                 int x=vc[i][j].first,y=vc[i][j].second,opx=x+y>>1,opy=x-y>>1,v=f[opx][opy];
47                 t1=Max(t1,v-x); t2=Max(t2,v+x);    t3=Max(t3,v-y); t4=Max(t4,v+y);
48             }
49         }
50         int jie=0;
51         for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
52             jie=Max(jie,f[i][j]);
53         write(jie);
54         return 0;
55     }
56 }
57 signed main(){return WSN::main();}
View Code

T3 english

这个题首先一看小的点线段树 可过,不过大的点就很吓人。。。。

 

 

所以,对于预处理,应该处理出 表示序列第i个数之前(包括他)的第j位一共有多少个1

表示a[i]是区间内最大值的左端点和右端点。

 

那么对于opt==1的情况,运用单调栈扫一边即可。还要注意疑惑对a[k]的贡献,有一个柿子:

 

 

k,l,r分别表示循环到的序列第k位,以其为最大值的左/右端点。i表示二进制位数。

然后就是opt==2。。。。

这是个可持久化trie树,还没学,于是花了一下午和一晚上学习了主席树和可持久化trie。。。太男了

trie树在查找的时候也是维护了一个类似前缀和的东西。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 #define write(X) printf("%lld\n",X)
 4 #define Min(A,B) ((A)<(B)?(A):(B))
 5 #define Max(A,B) ((A)>(B)?(A):(B))
 6 using namespace std;
 7 inline int read(){
 8     int x=0,f=1; char ch=getchar();
 9     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
10     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
11     return x*f;
12 }
13 const int mod=1e9+7,NN=1e5+5;
14 int n,opt,w[NN],ans1,ans2,ll[NN],rr[NN];
15 int pw[35],f[NN][35];
16 stack<int> s,t;
17 struct tree{
18     int ch[NN*22][2],siz[NN*22],root[NN*22],node;
19     void insert(int pre,int &now,int i,int x){//前驱点 当前点 最大递归位数 插入值
20         now=++node;
21         siz[now]=siz[pre];
22         if(i<0){siz[now]++;return;}
23         int bo=(x>>i)&1;
24         ch[now][bo^1]=ch[pre][bo^1];
25         insert(ch[pre][bo],ch[now][bo],i-1,x);
26         siz[now]=siz[ch[now][0]]+siz[ch[now][1]];
27     }
28     void insert(int x,int pos){insert(root[pos-1],root[pos],20,x);}
29     int query(int x,int y,int p){//在x~y区间内的1的个数
30         int pos=root[p],ans=0;
31         for(int i=20;i>=0;i--){
32             int a=(x>>i)&1,b=(y>>i)&1;
33             if(!b){
34                 ans+=siz[ch[pos][a^1]];
35                 pos=ch[pos][a];
36             }else pos=ch[pos][a^1];
37             if(!pos) break;
38         }
39         return ans;
40     }
41 }trie;
42 void work(int k,int l,int r){
43     for(int i=0;i<=20;i++){
44         int pre1=f[k][i]-f[l-1][i],pre0=k-l+1-pre1,nxt1=f[r][i]-f[k-1][i],nxt0=r-k+1-nxt1;
45         ans1=(ans1+pw[i]*(pre1*nxt0%mod+pre0*nxt1%mod)%mod*w[k]%mod)%mod;
46     }
47 }
48 void search(int k,int l,int r){
49     if(k-l<r-k){
50         for(int i=l;i<=k;i++)
51             ans2=(ans2+(trie.query(w[i],w[k],r)-trie.query(w[i],w[k],k-1))*w[k]%mod)%mod;
52     }
53     else{
54         for(int i=k;i<=r;i++)
55             ans2=(ans2+(trie.query(w[i],w[k],k)-trie.query(w[i],w[k],l-1))*w[k]%mod)%mod;
56     }
57 }
58 namespace WSN{
59     inline int main(){
60         n=read(),opt=read();
61         for(int i=1;i<=n;i++) {w[i]=read();trie.insert(w[i],i);}
62         pw[0]=1;
63         for(int i=1;i<=20;i++) pw[i]=2*pw[i-1];
64         for(int i=1;i<=n;i++){
65             while(!s.empty()&& w[i]>w[s.top()]) s.pop();
66             if(s.empty()) ll[i]=1;
67             else ll[i]=s.top()+1;
68             s.push(i);
69         }
70         for(int i=n;i>=1;i--){
71             while(!t.empty()&& w[i]>=w[t.top()]) t.pop();
72             if(t.empty()) rr[i]=n;
73             else rr[i]=t.top()-1;
74             t.push(i);
75         }
76         for(int i=1;i<=n;i++) for(int j=0;j<=20;j++)
77             {f[i][j]+=f[i-1][j]; if((w[i]>>j)&1) f[i][j]++;}
78         for(int i=1;i<=n;i++) work(i,ll[i],rr[i]);
79         for(int i=1;i<=n;i++) search(i,ll[i],rr[i]);
80         if(opt==1) write(ans1);
81         else if(opt==2) write(ans2);
82         else {write(ans1); write(ans2);} 
83         return 0;
84     }
85 }
86 signed main(){return WSN::main();}
87 // FILE *A=freopen("english3.in","r",stdin);
88 // FILE *B=freopen("aaa.out","w",stdout);
View Code

 

End


 

此次考试较为上次聪明了一点,总归是并没有执着于正解而不去打暴力,可是对于正解的思考还是较少,下次考试应该注意打暴力的速度和正确性以及留给正解的思考时间

 

posted @ 2021-07-11 15:50  雪域亡魂  阅读(80)  评论(0)    收藏  举报