noip模拟10[入阵曲·将军令·星空](luogu)
对于这次考试来说,总体考得还是不错的
就是有一个小问题,特判一定要判对,要不然和不判一样,甚至错了还会挂掉30分
还有一个就是时间分配问题,总是在前几个题上浪费太多时间,导致最后一个题完全没有时间思考
所以最后一个题我又成功的爆零了
下次冲第一,把分拿满
当然最重要的还是
关于这个考场上是想正解还是暴力的问题
我现在有了一个大概的思路,就是
不要浪费太多时间去想,一般保持在一个小时以内是可以的
当然这是在前10分钟已经把暴力思路弄出来的情况下(暴力程序可以先不着急打)
那下面就是正解时刻了
T1入阵曲
我个人认为这个题还是非常符合我的水平的
因为在考场上,我对这个题进行了百般的思考,虽然最终只有75pts,但是这个题确实让我成长很多
起码,我马上就要学会如何在考场上顺理成章的想出正解了
考场上想了很多,包括题目里的矩阵乘(即使我在5min内把它否掉了)
我想到了余数,也想到了余数如何使用,所以我准备用一个二维树状数组来实现 $ O(n^{2}logn) $的算法
但是最后我遗憾的发现,这空间就是给我2G我也开不出来
然后我就直接放弃这么做了,就打完暴力就溜走了,但是我的暴力比别人分高
我对全部都相同的情况进行了处理,然后拿到了15pts,嘿嘿
其实正解和我的思路是大同小异的,正解是$ O(n^3) $的,比我的树状数组慢,但是能A题
就是先枚举这个矩形的上下边界所在的行,就是$ x_1x_2\(的位置,此时复杂度是\) O(n^2) $的
然后下一层枚举列,此时列就可以当作一行算了,你已经求取了整个矩形的二位前缀和(别告诉我你这都没想到)
这时候我只需要记录余数相同的数有几个,统计答案就好了
就做完了,记得不要用$ memset \(因为那样的话复杂度就变成\) O(n^2k) $的了
由于$ k $非常的大是不能通过这个题的,所以我们记录一下用过的余数,一个一个清
\(code\)
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=405;
int n,m,k;
int a[N][N];
ll f[N][N],cnt[1000006],ans,tmp[N];
signed main(){
scanf("%d%d%d",&n,&m,&k);
for(re i=1;i<=n;i++)
for(re j=1;j<=m;j++){
scanf("%d",&a[i][j]);
f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-1]+a[i][j];
}
for(re i=0;i<=n;i++)
for(re j=i+1;j<=n;j++){
for(re o=0;o<=m;o++){
tmp[o]=(f[j][o]-f[i][o]+k)%k;
ans+=cnt[tmp[o]];
cnt[tmp[o]]++;
}
for(re o=0;o<=m;o++)cnt[tmp[o]]=0;
}
printf("%lld",ans);
}
答案很大,要用\(long long\)
\
T2将军令
我也不知道这是我第几次考场上想到正解但是没拿全分了
害说实话我这主要是特判错了,所以我以后在考场上对自己代码有信心的话,就不再特判了
我的方法说实话比较另类,正解其实很好理解,就是一个小贪心
\(O(2k)\)更新,然后总复杂度\(O(2kn)\)
其实我的方法比这种解法要少一个\(2k\),也就是\(O(n)\)的复杂度,因为k的范围实在太小,我的代码也没快到哪里去
$ 我的解法也是贪心,当然可以dp,机房大佬已经搞出dp方程了,dp[x][i]表示在x节点,向下覆盖i层的最小花费,$
$ 这样的话,如果在每个点加上权值,这个题也就有解了。。。。$
我用一个maxn数组记录,此刻这个点到根的路径上,或者是他的子树内,深度最大的没有被覆盖的点
所以我们能想到,如果dep-maxn==k的时候,我们就应该在这个点放置小队
然后我们就可以快快乐乐的进行dfs了
有四个转移条件,因为当你在这个点放置小队的时候,maxn=dep-k-1
而这个时候,我们就要对maxn和dep的关系进行讨论,因为从儿子转移过来,所以我们有四种情况、
设son[x]=y
1、maxn[y]>=dep[x]&&maxn[x]>=dep[x]那我们就可以直接取最小的maxn了,因为你能覆盖的我也能覆盖,你不能覆盖的我还能覆盖
2、maxn[y]>=dep[x]&&maxn[x]<dep[x]我们要看此时在其他儿子建立的小队能不能覆盖此时我这个儿子的最大没有覆盖的点
3、maxn[y]<dep[x]&&maxn[x]>=de[x]我们就看这个儿子建立的小队能不能覆盖其他儿子的最大maxn
4、maxn[y]<dep[x]&&maxn[x]<dep[x]那就直接去最大值
这样的话我们就可以由儿子向父亲\(O(1)\)转移了,就完了,记得maxn初值为dep
\(code\)
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
int n,k,t;
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
to[++rp]=y;
nxt[rp]=head[x];
head[x]=rp;
}
int maxn[N],maxm[N],dep[N],maxx,fa[N],lr;
int ans;
void dfs3(int x,int f){
maxn[x]=dep[x];
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==f)continue;
fa[y]=x;dep[y]=dep[x]+1;
dfs3(y,x);
if(maxn[y]<dep[x]&&maxn[x]>dep[x]&&dep[x]-maxn[y]-1>=maxn[x]-dep[x])maxn[x]=maxn[y];
if(maxn[y]<dep[x]&&maxn[x]<=dep[x])maxn[x]=min(maxn[x],maxn[y]);
if(maxn[y]>dep[x]&&maxn[x]<dep[x]&&maxn[y]-dep[x]>dep[x]-maxn[x]-1)maxn[x]=maxn[y];
if(maxn[y]>dep[x]&&maxn[x]>=dep[x])maxn[x]=max(maxn[x],maxn[y]);
}
if(maxn[x]-dep[x]==k){
ans++;maxn[x]=dep[x]-k-1;
}
}
signed main(){
scanf("%d%d%d",&n,&k,&t);
for(re i=1,x,y;i<n;i++){
scanf("%d%d",&x,&y);
add_edg(x,y);add_edg(y,x);
}
dep[1]=0;fa[1]=0;dfs3(1,0);
if(maxn[1]>=dep[1])ans++;
printf("%d",ans);
}
\
T3星空
啊啊啊这个题我改了好久啊
考完试半个小时我就把前两个题搞定了,最后这个题确实思维量很大,我根本看不懂题解
不是我看不懂是题解根本说的就不对,气死我了,害得我还看了半天,这ppt也不太行,说的没谱
还是靠我自己干出来
首先,我们看到这是个区间修改的操作,嘿嘿,第一眼线段树,然后我发现,虽然快,但是dp不了啊
后来想到,状压,一看40000,压不了果断放弃,然后暴力分都没拿到
但是正解确实是状压,,,为什么呢??
首先,区间换单点,对于序列来说,区间转单点的最好办法不过是差分和前缀和
而这个题明显是要用差分的,而取反操作,就是异或操作,我们令\(cf[i]=a[i] ^\wedge a[i-1]\)
我们操作完之后,所有\(cf[i]\)为\(1\)的地方,都是此时灯与前一个状态不同的地方,所以我们的目的就是把这些1都干掉
那么我们就发现,这个k之有8啊就算每一个灯都造成2个1的贡献,那么最大也只有16,这时候就是状压dp了
我们操作时只能将两个相距为特定值的点做取反操作,所以我们为了不做无谓的动作,每一次操作的两个数至少会有一个1
然后我们就可以想到,由其中一个1到达另外一个1的最小步数是多少,这时候我们就搬出好久不用的bfs
毕竟是最小步数,一层一层的搜找到的必然是最优解
然后我们就对这2k个1每个点分别进行bfs,复杂度是\(O(kmn)\)
连边就连这个点对于每一种操作的位置,包括前面和后面,都连上
然后我们就可以愉快的状压了
我们设dp[s]表示状态为s的最小步数,0表示没有消掉这一位上的1,1表示已经消掉
然后就转移就行了,其实这个dp我也想了好久,毕竟状压我也好久没打了
\(code\)
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=40005;
const int K=16;
int n,k,m;
int a[N],b[N],cf[N];
int off[N],cnt,pos[N];
int to[N*65*2],nxt[N*65*2],head[N],rp;
void add_edg(int x,int y){
to[++rp]=y;
nxt[rp]=head[x];
head[x]=rp;
}
bool vis[N];
ll dis[K+5][K+5],dp[1<<K];
struct node{
int x;ll len;
};
queue<node> q;
void bfs(int w,int hea){
memset(vis,false,sizeof(vis));
vis[w]=1;q.push((node){w,0});
while(!q.empty()){
node now=q.front();q.pop();
for(re i=head[now.x];i;i=nxt[i]){
int y=to[i];
if(vis[y])continue;
vis[y]=true;
if(cf[y])dis[hea][pos[y]]=now.len+1;
q.push((node){y,now.len+1});
}
}
}
signed main(){
scanf("%d%d%d",&n,&k,&m);
for(re i=1,x;i<=k;i++)scanf("%d",&x),a[x]=1;
for(re i=1;i<=m;i++)scanf("%d",&b[i]);
for(re i=1;i<=n+1;i++){
cf[i]=a[i]^a[i-1];
if(cf[i])off[++cnt]=i,pos[i]=cnt;
}
for(re i=1;i<=n+1;i++){
for(re j=1;j<=m;j++){
if(i+b[j]<=n+1)add_edg(i,i+b[j]);
if(i-b[j]>0)add_edg(i,i-b[j]);
}
}
memset(dis,0x3f,sizeof(dis));
for(re i=1;i<=cnt;i++)bfs(off[i],i);
memset(dp,0x3f,sizeof(dp));dp[0]=0;
for(re i=0;i<(1<<cnt);i++){
int p=0;
while(i&(1<<p))p++;
for(re j=p+1;j<=cnt;j++){
if(!(i&(1<<j)))
dp[i|(1<<j)|(1<<p)]=min(dp[i|(1<<j)|(1<<p)],dp[i]+dis[p+1][j+1]);
}
}
printf("%lld",dp[(1<<cnt)-1]);
}
然后就完了。。。。。。。