入阵曲

题解

应用了一种美妙移项思想,

我们先考虑在一维上的做法

维护前缀和$(sum[r]-sum[l-1])\%k==0$可以转化为

$sum[r]\% k==sum[l-1]\%k$开个桶维护一下即可

然后拓展到二维上

把两行之间所有行拍扁看作一维上的区间,

我们枚举两行和行之间所有列开个桶维护

$n^2 m$复杂度

    for(ll i=1;i<=n;i++)
for(ll j=1;j<=i;j++){
flag[0]=1;
for(ll q=1;q<=m;q++){
t[q]=(sum[i][q]-sum[j-1][q]+k)%k;
cnt+=flag[t[q]];
flag[t[q]]++;
}
for(ll q=1;q<=m;q++)
flag[t[q]]=0;
}

至于桶里flag[0]=1初始化含义

当你其他%==0的矩阵放进去时本身就符合条件,在桶里找配对之前就构成合法矩阵,这样做统计了所有%=0的情况

当然这样也行

    for(ll i=1;i<=n;i++)
for(ll j=1;j<=i;j++){
for(ll q=1;q<=m;q++){
t[q]=(sum[i][q]-sum[j-1][q]+k)%k;
cnt+=flag[t[q]];
flag[t[q]]++;
}
cnt+=flag[0];
for(ll q=1;q<=m;q++)
flag[t[q]]=0;
}

将军令

题解

$45\%$算法

计算k==1

和小胖收皇宫类似,

思考dp数组含义

$f[x][0]$表示被父亲管辖   $f[x][1]$表示被自己管辖   $f[x][2]$表示被自己儿子管辖

f数组转移

若x被父亲管辖,那么儿子必须被自己管辖或者被儿子的儿子管辖

$f[x][0]=\sum\limits_{y}^{y\in son}min(f[y][1],f[y][2])$

若x被自己管辖,那么x转移随意

$f[x][1]=\sum\limits_{y}^{y\in son} min(f[y][1],f[y][2],f[y][0])$

若x被儿子管辖,那么儿子可以被自己管辖或者被儿子的儿子管辖

但如果所有儿子的儿子代价都比选自己代价小,我们需要强制选出一个$f[y][1]-f[y][2]$差值最小的更新

代码稍微体会一下

void dp(ll x){
if(!son[x]) {
f[x][1]=1;
f[x][2]=0x7ffffff;
f[x][0]=0;
return ;
}
vis[x]=1;
ll sum=0,sum2=0,sum3=0,pan=0,mix=0x7ffffff;
for(ll i=head[x];i;i=nxt[i]){
ll y=ver[i];
if(vis[y]) continue;
dp(y);
sum+=min(f[y][0],min(f[y][1],f[y][2]));
sum2+=min(f[y][2],f[y][1]);
sum3+=min(f[y][1],f[y][2]);
if(f[y][1]<=f[y][2]) pan=1;
else
mix=min(mix,f[y][1]-f[y][2]);
}
f[x][1]=sum+1;
f[x][0]=sum2;
if(pan==1) f[x][2]=sum3;
else f[x][2]=sum3+mix;
}

$75\%$算法

计算k==2

还是思考dp数组含义

如果还是像上一个那样定义要写死

换一种0表示被自己守,1表示被儿子守(至少选了一个儿子),2被孙子守(至少选了一个孙子),3子孙全被覆盖自己没有,4孙子全被覆盖(儿子可以被覆盖可以不被覆盖)自己没有

还是思考转移

被自己守x转移还是随意$f[x][0]=\sum\limits_{y}^{y\in son} min(f[y][0],f[y][1],f[y][2],f[y][3],f[y][4])$

被儿子守x转移比较复杂

儿子需要自保,因为儿子可以管辖自己兄弟,所以随意选,所以可以选到$f[y][3]$

$f[x][1]=\sum\limits_{y}^{y\in son} min(f[y][0],f[y][1],f[y][2],f[y][3])$

也和k==1类似,显然我们还是要选出来一个最小差值

被自己孙子守,那么自己儿子需要自保,$f[y][1],f[y][2],f[y][0]$都满足条件

子孙全被覆盖自己没有,那么就是儿子孙子全被覆盖,儿子本身也要被覆盖,

$f[y][0]$  $f[y][1]$   $f[y][2]$          (显然$f[y][3]$不行)

孙子全被覆盖,那么就是孙子全被覆盖,那么可以是$f[y][3]$,$f[y][1]$,$f[y][0]$,$f[y][2]$

代码稍微体会一下(这个代码有误,我并不能调出来)

void dp2(ll x){
//1表示被自己守,2表示被儿子,3被孙子,4子孙全有自己无,5孙子全有自己无
if(!son[x]){
f2[x][4]=0x7fffffff;
f2[x][5]=0x7fffffff;
f2[x][1]=1;
f2[x][2]=0;
f2[x][3]=0;
return ;
}
vis[x]=1;
ll sum1=0,sum2=0,sum3=0,sum4=0,sum5=0,pan=0,mix1=0x7fffffff,pan2=0,mix2=0x7ffffff;
for(ll i=head[x];i;i=nxt[i]){
ll y=ver[i];
if(vis[y]) continue;
dp2(y);
sum1+=min(min(f2[y][1],f2[y][2]),min(f2[y][3],min(f2[y][4],f2[y][5])));
sum2+=min(min(f2[y][1],f2[y][2]),min(f2[y][3],f2[y][4]));
if(f2[y][1]<=f2[y][2]&&f2[y][1]<=f2[y][3]&&f2[y][1]<=f2[y][4])
pan=1;
else
mix1=min(mix1,f2[y][1]-min(f2[y][2],min(f2[y][3],f2[y][4])));
sum3+=min(f[y][1],min(f[y][2],f[y][3]));
if(f2[y][2]<=f2[y][1]&&f2[y][2]<=f2[y][3])
pan2=1;
else
mix2=min(mix2,f2[y][2]-min(f2[y][1],f2[y][3]));
sum4+=min(min(f2[y][1],f2[y][2]),f2[y][3]);
sum5+=min(min(f2[y][1],f2[y][2]),min(f2[y][3],f2[y][4]));
}
f2[x][1]=1+sum1;
if(pan==1)
f2[x][2]=sum2;
else f2[x][2]=sum2+mix1;
if(pan2==1)
f2[x][3]=sum3;
else f2[x][3]=sum3+mix2;
f2[x][4]=sum4;
f2[x][5]=sum5;
}

思考怎么优化

dp数组含义再次转变

$f[x][w]$表示$f$中<=w最小的数

例如$f[x][4]$表示$min(f[x][1],f[x][2],f[x][3],f[x][4],f[x][0])$

转移类似

$f[x][0]=\sum\limits_{y}^{y\in son} f[y][4]  $$ +1$

$f[x][3]=\sum\limits_{y}^{y\in son} f[y][2]  $

$f[x][2]=\sum\limits_{y}^{y\in son} f[y][3]  $$+差值$

$f[x][4]=\sum\limits_{y}^{y\in son} f[y][3]  $

$f[x][1]=\sum\limits_{y}^{y\in son} f[x][4]  $$+差值$

最后1 2 3 4 互相取min具体看代码

再次感受一下

void dp2(ll x){
if(!son[x]){
f2[x][0]=1;
f2[x][1]=f2[x][2]=1;f2[x][3]=f2[x][4]=0;
f2[x][1]=0x7fffffff;
return ;
}
vis[x]=1;
f2[x][0]=1;
f2[x][1]=f2[x][2]=f2[x][3]=f2[x][4]=0;
ll pan=0,mix1=0x7fffffff,pan2=0,mix2=0x7ffffff;
for(ll i=head[x];i;i=nxt[i]){
ll y=ver[i];
if(vis[y]) continue;
dp2(y);
f2[x][0]+=f2[y][4];
f2[x][3]+=f2[y][2];
f2[x][4]+=f2[y][3];
mix1=min(f2[y][0]-f2[y][3],mix1);
mix2=min(f2[y][1]-f2[y][2],mix2);
}
f2[x][1]=f2[x][4]+mix1;
f2[x][2]=min(f2[x][3]+mix2,min(f2[x][0],f2[x][1]));
f2[x][3]=min(f2[x][3],f2[x][2]);
f2[x][4]=min(f2[x][3],f2[x][4]);
// printf("mix1=%lld 2=%lld x=%lld f[][1]=%lld [2]=%lld [3]=%lld [4]=%lld [0]=%lld\n",mix1,mix2,x,f2[x][1],f2[x][2],f2[x][3],f2[x][4],f2[x][0]);
}

$100\%$算法

dp我是打不出来

应该会有别的大神打出来dp100分

那么正解是什么神奇的算法呢?

简单贪心!

啊啊啊啊啊又是贪心,我又没有看出来它是贪心,awsl,我太菜了

步骤分比正解难的多得多

贪心10分钟改完AC,每次找到最深得节点找它的k级父亲

实现不要想复杂,暴力改,暴力跳即可

你会发现你比dp还要快!!!!!!!!!!!!!!!!!!!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 1010101
ll n,k,t,tot=0,sum1=0,sum2=0,sumji=0,sumou=0,skriller=0;
ll head[A],nxt[A],ver[A],deep[A],son[A],f[A][3],fa[A];
ll f2[A][10];
struct node{
ll deep,x;
friend bool operator < (node a,node b){
return a.deep<b.deep;
}
}point[A];
priority_queue<node> q;
bool vis[A];
void add(ll x,ll y){
nxt[++tot]=head[x];
head[x]=tot;
ver[tot]=y;
}
ll read(){
ll f=1,x=0;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
void dfs(ll x,ll de){
vis[x]=1;
point[x].deep=de;
point[x].x=x;
q.push(point[x]);
for(ll i=head[x];i;i=nxt[i]){
ll y=ver[i];
if(vis[y]) continue;
dfs(y,de+1);
son[x]++;
fa[y]=x;
}
}
void dp(ll x){
if(!son[x]) {
f[x][1]=1;
f[x][2]=0x7ffffff;
f[x][0]=0;
return ;
}
vis[x]=1;
ll sum=0,sum2=0,sum3=0,pan=0,mix=0x7ffffff;
for(ll i=head[x];i;i=nxt[i]){
ll y=ver[i];
if(vis[y]) continue;
dp(y);
sum+=min(f[y][0],min(f[y][1],f[y][2]));
sum2+=min(f[y][2],f[y][1]);
sum3+=min(f[y][1],f[y][2]);
if(f[y][1]<=f[y][2]) pan=1;
else
mix=min(mix,f[y][1]-f[y][2]);
}
f[x][1]=sum+1;
f[x][0]=sum2;
if(pan==1) f[x][2]=sum3;
else f[x][2]=sum3+mix;
}
ll find(ll x){
ll w=1;
while(w<=k){
w++;
x=fa[x];
}
return x;
}
void dp2(ll x){
if(!son[x]){
f2[x][0]=1;
f2[x][1]=f2[x][2]=1;f2[x][3]=f2[x][4]=0;
f2[x][1]=0x7fffffff;
return ;
}
vis[x]=1;
f2[x][0]=1;
f2[x][1]=f2[x][2]=f2[x][3]=f2[x][4]=0;
ll pan=0,mix1=0x7fffffff,pan2=0,mix2=0x7ffffff;
for(ll i=head[x];i;i=nxt[i]){
ll y=ver[i];
if(vis[y]) continue;
dp2(y);
f2[x][0]+=f2[y][4];
f2[x][3]+=f2[y][2];
f2[x][4]+=f2[y][3];
mix1=min(f2[y][0]-f2[y][3],mix1);
mix2=min(f2[y][1]-f2[y][2],mix2);
}
f2[x][1]=f2[x][4]+mix1;
f2[x][2]=min(f2[x][3]+mix2,min(f2[x][0],f2[x][1]));
f2[x][3]=min(f2[x][3],f2[x][2]);
f2[x][4]=min(f2[x][3],f2[x][4]);
// printf("mix1=%lld 2=%lld x=%lld f[][1]=%lld [2]=%lld [3]=%lld [4]=%lld [0]=%lld\n",mix1,mix2,x,f2[x][1],f2[x][2],f2[x][3],f2[x][4],f2[x][0]);
}
void dfs2(ll x,ll fa,ll de){
vis[x]=1;
if(de==k) return ;
for(ll i=head[x];i;i=nxt[i]){
ll y=ver[i];
if(y==fa) continue;
dfs2(y,x,de+1);
}
}
int main(){
n=read(),k=read(),t=read();
// printf("%lld\n",k);
if(k==0){
for(ll i=1,a,b;i<=n-1;i++){
a=read(),b=read();
}
printf("%lld\n",n);
return 0;
}
/* if(k==1){
for(ll i=1,a,b;i<=n-1;i++){
a=read(),b=read();
add(a,b);
add(b,a);
}
dfs(1,1);
memset(vis,0,sizeof(vis));
dp(1);
printf("%lld\n",min(f[1][1],f[1][0]));
return 0;
}
*/ if(k==2){
memset(f2,0x3f,sizeof(f2));
for(ll i=1,a,b;i<=n-1;i++){
a=read(),b=read();
add(a,b);
add(b,a);
}
dfs(1,1);
memset(vis,0,sizeof(vis));
dp2(1);
printf("%lld\n",f2[1][2]);
return 0;
}
else{
for(ll i=1,a,b;i<=n-1;i++){
a=read(),b=read();
add(a,b);
add(b,a);
}
dfs(1,1);
memset(vis,0,sizeof(vis));
while(!q.empty()){
ll x=q.top().x;
q.pop();
if(vis[x]) continue;
ll f=find(x);
// printf("x=%lld f=%lld\n",x,f);
dfs2(f,0,0);
skriller++;
}
printf("%lld\n",skriller);
}
}

星空

题解

首先如果翻转我们楞翻转一次复杂度最高n那么考虑优化

我们将取反转化为异或

思考$1 xor 1=0$

$0 xor 1=1$

那么取反我们就转化为了$xor 1$

定义差分数组为$b[i]=a[i] xor a[i+1]$

整段区间$xor1$差分(第一次看到异或的差分),我们转化为$l xor1$ $r+1xor1$

我们不可能白白翻转一段全是$1$的

我们翻转至少有$1$个零

翻转两端都有$0$那么就可以看作消去

两端只有一端有$0$那么可以看作移动

那么问题就转化为了如何最少移动消去使所有$0$变为$1$

处理出任意两个点之间消去代价(可以完全背包,把每个操作换成$+ $,$-$ 两个代价)

    for(ll i=1;i<=m;i++)
for(ll j=a[i];j<=n;j++)
d[j]=min(d[j-a[i]]+1,d[j]); for(ll i=1;i<=m;i++)
for(ll j=n-a[i];j;j--)
d[j]=min(d[j+a[i]]+1,d[j]);

考虑k很小,然后状压解决把所有点消去代价

    memset(f,0x7f,sizeof(f));
f[0]=0;
for(ll i=0;i<ci[cnt];i++){
for(ll j=0;j<cnt;j++){
if(!(ci[j]&i)){ for(ll k=j+1;k<cnt;k++){
if((!(ci[k]&i))){
if(f[i]>100000000) continue;
else {
// printf(" i=%lld j=%lld k=%lld cij=%lld cik=%lld f=%lld\n",i,j,k,ci[j],ci[k],f[i]);
f[i|ci[j]|ci[k]]=min(f[i|ci[j]|ci[k]],f[i]+d[abs(pos[j]-pos[k])]);
// printf("f=%lld d=%lld\n",f[i],d[abs(pos[j]-pos[k])]);
}
}
}
break;
}
}
}

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 111111
ll b[A],d[A],Xor[A],pos[A],ci[A],f[A],a[A];
ll cnt,m,n,k;
inline void init()
{
memset(d,0x3f,sizeof(d));
d[0]=0;
for(ll i=1;i<=m;i++)
for(ll j=a[i];j<=n;j++)
d[j]=min(d[j-a[i]]+1,d[j]); for(ll i=1;i<=m;i++)
for(ll j=n-a[i];j;j--)
d[j]=min(d[j+a[i]]+1,d[j]);
for(ll i=1;i<=n;i++){
if(Xor[i])
pos[cnt++]=i;
}
/* for(ll i=1;i<=n;i++){
printf("%lld\n",d[i]);
}
printf("cnt=%lld\n",cnt);
*/}
inline void dp(){
memset(f,0x7f,sizeof(f));
f[0]=0;
for(ll i=0;i<ci[cnt];i++){
for(ll j=0;j<cnt;j++){
if(!(ci[j]&i)){ for(ll k=j+1;k<cnt;k++){
if((!(ci[k]&i))){
if(f[i]>100000000) continue;
else {
// printf(" i=%lld j=%lld k=%lld cij=%lld cik=%lld f=%lld\n",i,j,k,ci[j],ci[k],f[i]);
f[i|ci[j]|ci[k]]=min(f[i|ci[j]|ci[k]],f[i]+d[abs(pos[j]-pos[k])]);
// printf("f=%lld d=%lld\n",f[i],d[abs(pos[j]-pos[k])]);
}
}
}
break;
}
}
}
}
int main(){
ci[0]=1;
for(ll i=1;i<=30;i++)
ci[i]=ci[i-1]<<1/*,printf("ci[%lld]=%lld\n",i,ci[i])*/;
scanf("%lld%lld%lld",&n,&k,&m);
n++;
for(ll i=1,ak;i<=k;i++){
scanf("%lld",&ak);
b[ak]^=1;
}
for(ll i=1;i<=n;i++){
Xor[i]=b[i-1]^b[i];
}
for(ll i=1;i<=m;i++){
scanf("%lld",&a[i]);
}
init();
dp();
printf("%lld\n",f[ci[cnt]-1]);
}

NOIP模拟测试17「入阵曲&#183;将军令&#183;星空」的更多相关文章

  1. NOIP模拟测试14「旋转子段·走格子·柱状图」

    旋转子段 连60分都没想,考试一直肝t3,t2,没想到t1最简单 我一直以为t1很难,看了题解发现也就那样 题解 性质1 一个包含a[i]旋转区间值域范围最多为min(a[i],i)----max(a ...

  2. NOIP模拟测试5「星际旅行·砍树·超级树」

    星际旅行 0分 瞬间爆炸. 考试的时候觉得这个题怎么这么难, 打个dp,可以被儿子贡献,可以被父亲贡献,还有自环,叶子节点连边可以贡献,非叶子也可以贡献,自环可以跑一回,自环可以跑两回, 关键是同一子 ...

  3. NOIP模拟测试17&18

    NOIP模拟测试17&18 17-T1 给定一个序列,选取其中一个闭区间,使得其中每个元素可以在重新排列后成为一个等比数列的子序列,问区间最长是? 特判比值为1的情况,预处理比值2~1000的 ...

  4. 8.11 NOIP模拟测试17 入阵曲+将军令+星空

    T1 入阵曲 前缀和维护可以得60分 f[x1][y1][x2][y2]=sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];  O(n4) ...

  5. NOIP模拟测试19「count·dinner·chess」

    反思: 我考得最炸的一次 怎么说呢?简单的两个题0分,稍难(我还不敢说难,肯定又有人喷我)42分 前10分钟看T1,不会,觉得不可做,完全不可做,把它跳了 最后10分钟看T1,发现一个有点用的性质,仍 ...

  6. NOIP模拟测试21「折纸&#183;不等式」

    折纸 题解 考试时无限接近正解,然而最终也只是接近而已了 考虑模拟会爆炸,拿手折纸条试一试,很简单 考你动手能力 代码 #include<bits/stdc++.h> using name ...

  7. NOIP模拟测试13「矩阵游戏&#183;跳房子&#183;优美序列」

    矩阵游戏 考试时思路一度和正解一样,考试到最后还是打了80分思路,结果80分打炸了只得了40分暴力分 题解 算出来第一列的总值,每次通过加每两列之间的差值得出下一列的总值 算第一列我们只需要让当前点* ...

  8. NOIP模拟测试11「string&#183;matrix&#183;big」

    打的big出了点小问题,maxx初值我设的0然后少了10分 第二题暴力打炸 第一题剪了一些没用的枝依然40分 总分70 这是一次失败的考试 string 想到和序列那个题很像,但我没做序列,考场回忆学 ...

  9. NOIP模拟测试17

    T1:入阵曲 题目大意:给定一个N*M的矩形,问一共有多少个子矩形,使得矩形内所有书的和为k的倍数. 60%:N,M<=80 枚举矩形的左上角和右下角,用二维前缀和求出数字之和. 时间复杂度$O ...

随机推荐

  1. Centos7下搭建gitbook环境踩坑记录

    1.安装npm yum -y install npm 2.配置npm仓 npm config set registry https://mirrors.tencent.com/npm/ 3.安装git ...

  2. Window内核学习之保护模式基础

    段寄存器 段寄存器有6个分别是 cs,ss,ds,es,fs,gs.这些段寄存器包含16位的可见部分和80位的隐藏部分,共90位. 16位的可见部分就是我们知道的cs等段寄存器的值,我们可以在od中查 ...

  3. JavaScrip条件表达式优化

    目录 1,前言 2,多条件if语句优化 3,参数默认值 4,Switch语句优化 1,前言 今早看了一篇文章<JavaScrip实现:如何写出漂亮的条件表达式>,原创于:华为云开发者社区, ...

  4. 关于有符号数和无符号数的转换 - C/C++

    转载自:http://www.94cto.com/index/Article/content/id/59973.html 1.引例: 今天在做了一道关于有符号数和无符号数相互转换及其左移/右移的问题, ...

  5. C++基于armadillo im2col的实现

    最近学习CNN,需要用到im2col这个函数,无奈网上没有多少使用armadillo的例子,而且armadillo库中似乎也没有这个函数,因此自己写了. im2col的原理网上一大把,我懒得写了. 1 ...

  6. [bug] Python AttributeError: module 'web' has no attribute 'application'

    原因 文件名是web.py,与包名web冲突 解决 重命名文件,再运行

  7. [项目] 淘淘商城 Part.2

    商品列表查询 Easyui 商品添加 商品类目选择 图片上传 富文本编辑器使用 添加的实现 展示首页 略 分页插件 在SqlMapConfig.xml,配置一个plugin 在sql语句执行之前,添加 ...

  8. 【转载】Linux踢出其他正在SSH登陆用户

    Linux踢出其他正在SSH登陆用户     在一些生产平台或者做安全审计的时候往往看到一大堆的用户SSH连接到同一台服务器,或者连接后没有正常关闭进程还驻留在系统内.限制SSH连接数与手动断开空闲连 ...

  9. 用urllib库几行代码实现最简单爬虫

    """ 使用urllib.request()请求一个网页内容,并且把内容打印出来. """ from urllib import reque ...

  10. MySQL 查询操作

    目录 基本语法 查询常量 查看表达式 查询函数 查询指定字段 查询所有列 列别名 表别名 条件查询 条件查询运算符 逻辑查询运算符 排序与分页 排序查询(order by) 排序方式 limit 分组 ...