NOIP模拟测试17

T1:入阵曲

  题目大意:给定一个N*M的矩形,问一共有多少个子矩形,使得矩形内所有书的和为k的倍数。

  60%:N,M<=80

    枚举矩形的左上角和右下角,用二维前缀和求出数字之和。

    时间复杂度$O(N^4)$

  100%

    我们发现美剧每个矩形的复杂度已经为N4,那么我们改为枚举矩形的边界。先枚举左右边界,再从上到下枚举下边界,两个矩形相减便可得出所有矩形,枚举时将每个矩形的区间和压入桶中,每次枚举时直接在桶中查询,左右边界更换时清桶。注意下边界要从0开始枚举。

    时间复杂度$O(N^3)$

    记得要卡常。

Code:

 1 #include<iostream>
 2 #include<cstdio>
 3 const int L=1<<20|1;
 4 char buffer[L],*S,*T;
 5 #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
 6 #define LL unsigned long long
 7 #define rint register unsigned int
 8 using namespace std;
 9 const unsigned int N=410;
10 int n,m,d;
11 int s[N][N];
12 unsigned int dfn[2000010],t[2000010];
13 inline int read()
14 {
15     rint s=0;char c=getchar();
16     while(c<48||c>57) c=getchar();
17     while(c>=48&&c<=57)s=(s<<3)+(s<<1)+(c^48),c=getchar();
18     return s;
19 }
20 int main()
21 {
22     n=read();m=read();d=read();
23     for(rint i=1;i<=n;++i){
24         for(rint j=1;j<=m;++j)
25             s[i][j]=(s[i-1][j]+s[i][j-1]-s[i-1][j-1]+read())%d;
26     }
27     LL ans=0;rint df=0;
28     for(rint i=1;i<=m;++i){
29         for(rint j=i;j<=m;++j){
30             dfn[0]=++df;t[0]=1;
31             for(rint k=1;k<=n;++k){
32                 register int now=((s[k][j]-s[k][j-i])%d+d)%d;
33                 if(dfn[now]!=df){
34                     dfn[now]=df;t[now]=1;
35                 }
36                 else{
37                     ans+=t[now];++t[now];
38                 }
39             }
40         }
41     }
42     printf("%lld\n",ans);
43     return 0;
44 }
T1

 

T2:将军令:

  题目大意:给定一棵树,和一个距离K,在某个节点驻扎军队可以占领所有与该节点距离不超过K的点,求最少要驻扎多少军队。

  此题和小胖守皇宫类似,不过权值均为一,可以用贪心做。

  将所有的点按深度排序,从前往后扫,遇到未标记的点,则从他的K辈祖先处开始进行dfs,暴力标记,将ans加一,扫到最后的结果即是答案。

  贪心策略证明:

    由于每次找的是深度最大的点,那么每次dfs一定能标记到当前子树的所有儿子,同时要想使其他子树需要的军队尽可能少,则应再次条件下尽可能向上,越向上越能标记更多的点。证明完毕,贪心策略正确。

  时间复杂度$O(NK)$

Code:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=100010;
 6 int n,k,t,m=0,ans=0;
 7 int fi[N],d[N],f[N];
 8 bool v[N];
 9 struct edge{
10     int v,ne;
11 }e[N<<1];
12 struct point{
13     int d,id;
14 }p[N];
15 void add(int x,int y)
16 {
17     e[++m].v=y;
18     e[m].ne=fi[x];fi[x]=m;
19 }
20 bool comp(const point a1,const point a2)
21 {
22     return a1.d>a2.d;
23 }
24 int read()
25 {
26     int s=0;char c=getchar();
27     while(c<'0'||c>'9') c=getchar();
28     while(c>='0'&&c<='9'){
29         s=(s<<3)+(s<<1)+c-'0';
30         c=getchar();
31     }
32     return s;
33 }
34 void dfs(int x,int pa)
35 {
36     p[x].id=x;
37     for(int i=fi[x];i!=0;i=e[i].ne){
38         int y=e[i].v;
39         if(y==pa) continue;
40         f[y]=x;
41         p[y].d=p[x].d+1;
42         dfs(y,x);
43     }
44 }
45 void work(int x,int pa,int d)
46 {
47     v[x]=true;
48     if(d==0) return;
49     for(int i=fi[x];i!=0;i=e[i].ne){
50         int y=e[i].v;
51         if(y==pa) continue;
52         work(y,x,d-1);
53     }
54 }
55 int main()
56 {
57     n=read();k=read();t=read();
58     for(int i=1;i<n;i++){
59         int x=read(),y=read();
60         add(x,y);add(y,x);
61     }
62     p[1].d=0;dfs(1,0);
63     sort(p+1,p+n+1,comp);
64     for(int i=1;i<=n;i++){
65         int x=p[i].id;
66         if(!v[x]){
67             for(int i=1;i<=k&&f[x]!=0;i++)
68                 x=f[x];
69             work(x,0,k);ans++;
70         }
71     }
72     printf("%d\n",ans);
73     return 0;
74 }
T2

 

T3:星空

  题目大意:给一个0/1串,每次能将一个给定长度的区间去反,求最少要几次将整个串变为零。

  0/1串长度:N<=40000,区间长度数:M<=64,1的个数:K<=8

  24%做法:N<=16

    直接状压,跑BFS即可。DFS会T飞

  复杂度$O(N*2^N)$

  100%做法:

    我们发现相对于N,K很小,可以用来做做文章。

  开始转化题意:

    我们设灯亮为0,灯灭为1,则序列全为0时,所有灯都点亮。

    不难发现当N很大时,该序列中仅有K个1,我们从0位开始将每一位与其后一位亦或,则最多有2K个数与前一位不同,最多有2K个1,每次选择一段区间[L,R]取反,仅会改变第L-1位和第R位的值,相当于将序列中一定距离的点同时取反。

  题意转化为:给一个0/1串,每次选择相距给定长度的点同时取反,最少几次能将整个串全变为0。

  若选择的两个书均为0,没有任何意义;

  若选择的两个数均为1,则相互抵消;

  若其中一个数为0,另一个数为1,相当与将1移动一定距离。

  题意再次转化为:数轴上有最多2K个点,每次将一个点移动一段距离,两个点碰在一起后同时消失,最少几次能让所有点消失。

  此时K的范围状压可以接受,BFS算出两点间移动的最少次数,用状压DP简单解决。

  时间复杂度$O(MNK+K*2^K)$或$O(MNK+K^2*2^K)$

Code:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 const int N=40010;
 7 const int inf=99999999;
 8 int n,k,m,tot=0;
 9 int a[N],b[N],v[N],c[20],d[20][20],e[100];
10 int dp[1<<16];
11 queue<int> q;
12 int read()
13 {
14     int s=0;char c=getchar();
15     while(c<'0'||c>'9') c=getchar();
16     while(c>='0'&&c<='9'){
17         s=(s<<3)+(s<<1)+c-'0';
18         c=getchar();
19     }
20     return s;
21 }
22 void bfs(int pos)
23 {
24     int now=c[pos];
25     memset(v,0,sizeof(v));
26     v[now]=1;q.push(now);
27     while(!q.empty()){
28         int x=q.front();q.pop();
29         for(int i=1;i<=m;i++){
30             int y=x+e[i];
31             if(y<=n+1&&v[y]==0){
32                 v[y]=v[x]+1;q.push(y);
33             }
34             y=x-e[i];
35             if(y>=1&&v[y]==0){
36                 v[y]=v[x]+1;q.push(y);
37             }
38         }
39     }
40     for(int i=1;i<=tot;i++){
41         if(i!=pos){
42             if(v[c[i]]==0) d[pos][i]=inf;
43             else d[pos][i]=v[c[i]]-1;
44         }
45     }
46 }
47 int main()
48 {
49     n=read();k=read();m=read();
50     for(int i=1;i<=k;i++){
51         int x=read();
52         a[x]=1;
53     }
54     for(int i=0;i<=n;i++){
55         b[i]=a[i]^a[i+1];
56         if(b[i]==1) c[++tot]=i+1;    
57     }
58     for(int i=1;i<=m;i++)
59         e[i]=read();
60     for(int i=1;i<=tot;i++)
61         bfs(i);
62     for(int i=1;i<(1<<tot);i++)
63         dp[i]=inf;
64     for(int i=0;i<(1<<tot);i++){
65         for(int j=1;j<=tot;j++){
66             if(((i>>(j-1))&1)==1) continue;
67             for(int g=1;g<=tot;g++){
68                 if(g==j) continue;
69                 if(((i>>(g-1))&1)==1) continue;
70                 dp[i|(1<<(j-1))|(1<<(g-1))]=min(dp[i|(1<<(j-1))|(1<<(g-1))],dp[i]+d[j][g]);
71             }
72         }
73     }
74     printf("%d\n",dp[(1<<tot)-1]);
75     return 0;
76 }
T3

 

 

posted @ 2019-08-12 19:16  hz_Rockstar  阅读(169)  评论(0编辑  收藏  举报