2019-8-11 考试总结

A. 入阵曲

直接二维前缀和复杂度$O(n^4)$可得$60$。

另外的性质分数可得$15$,暴力$75$。

正解是$O(n^3)$。

设右下角格子坐标为$(i,j)$,左上角格子为$(p,k)$。

那么满足条件的必须是$(sum[i][j]-sum[i][k-1]-sum[p-1][j]+sum[p-1][k-1])\% k=0$,

就是$sum[i][j]-sum[i][k-1]=sum[p-1][j]-sum[p-1][k-1]$,

然后这个可以用一个桶存余数为$i$的数的个数$S[i]$,

然后先枚举列,再枚举行,把每一行的$sum[i][j]-sum[i][k-1]$存下来。

然后就$O(1)$查找,复杂度$O(n^3)$。

丑陋的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define Reg register
#define Maxn 450
using namespace std;
int n,m,k,W[Maxn][Maxn],sum[Maxn][Maxn],g[1000050];
long long ans=0;
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(Reg int i=1;i<=n;++i)
    {
        for(Reg int j=1;j<=m;++j)
        {
            scanf("%d",&W[i][j]);
            sum[i][j]=(sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+W[i][j]+k)%k;
        }
    }
    for(Reg int j=1;j<=m;++j)
    {
        for(Reg int p=j;p>=1;--p)
        {
            for(Reg int i=1,x;i<=n;++i)
            {
                x=(sum[i][j]-sum[i][p-1]+k)%k;
                ans+=g[x]; if(x==0) ++ans;
                ++g[x];
            }
            for(Reg int i=1,x;i<=n;++i)
            {
                x=(sum[i][j]-sum[i][p-1]+k)%k;
                --g[x];
            }
        }
    }
    printf("%lld",ans);
    return 0;
}
View Code

B. 将军令

好吧,又是贪心。

没想到要贪心,想拿$k=2$之前的分,然后$dp$状态都没定对。

最后就拿了$40$分。

每次贪心找最深的没有被守的节点的$k$次祖先,然后守他的$k$次祖先。

这样贪心为什么对的,

因为如果对于最深的节点,如果不守他的$k$次祖先,那么肯定会守深度更大的一个节点,

而深度更大的节点肯定不比它更优。

这可以感性地理解一下。

不知道能不能严谨地证明。

丑陋的代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define Reg register
#define Maxn 100050
#define INF 0x7ffffffff
#define _min(x,y) ((x)<(y)?(x):(y))
using namespace std;
bool vis[Maxn];
int n,k,t,ans,tot,fir[Maxn],dep[Maxn],num[Maxn],fat[Maxn],vap[Maxn];
struct Tu {int st,ed,next;} lian[Maxn*2];
vector<vector<int> > son(Maxn);
void add(int x,int y)
{
    lian[++tot].st=x;
    lian[tot].ed=y;
    lian[tot].next=fir[x];
    fir[x]=tot;
    return;
}
bool comp(int x,int y) {return dep[x]>dep[y];}
void dfs(int x)
{
    vis[x]=1;
    for(Reg int i=fir[x];i;i=lian[i].next)
    {
        if(vis[lian[i].ed]) continue;
        dep[lian[i].ed]=dep[x]+1;
        dfs(lian[i].ed);
        fat[lian[i].ed]=x;
        son[x].push_back(lian[i].ed);
    }
    return;
}
void update(int x,int len,int k)
{
    vis[x]=1;
    if(len==0||vap[x]>len) return;
    vap[x]=len;
    for(Reg int i=0,p;i<son[x].size();++i)
    {
        p=son[x][i];
        if(p!=k) update(p,len-1,0);
    }
    update(fat[x],len-1,x);
    return;
}
int main()
{
//    freopen("text.in","r",stdin);
    scanf("%d%d%d",&n,&k,&t);
    for(Reg int i=1,x,y;i<=n-1;++i)
    {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    dep[1]=1; dfs(1);
    for(Reg int i=1;i<=n;++i) num[i]=i;
    sort(num+1,num+n+1,comp);
    memset(vis,0,sizeof(vis));
    for(Reg int i=1;i<=n;++i)
    {
        if(!vis[num[i]])
        {
            int now=num[i];
            for(Reg int j=1;j<=k;++j)
            {
                if(fat[now]) now=fat[now];
                else break;
            }
            ++ans;
            update(now,k,0);
        }
    }
    printf("%d",ans);
    return 0;
}
View Code

C. 星空

正解:

把亮的灯泡看作$1$,不亮的灯泡看作$0$,

每次修改会改一个连续的长度为$L$的区间,把区间中的灯泡异或$1$,

把区间差分,设$b[i]=a[i]^a[i-1]$,并且让$a[0]=1$,

为了方便,把$b[i]^1$。

那么现在要做的就是修改左右端点,让整个$b$序列变成$1$。

修改区间时要修改的是$i$和$i+len$。

每次修改肯定要修改有$0$的地方,否则肯定不优。

那么如果修改的两个端点一个是$0$一个是$1$,那么就相当于把$1$变成$0$,把$0$变成$1$。

如果两个都是$0$,那么这两个$0$都会变成$1$,

下面是玄学操作:

把$0->1$看作点的移动,第二种情况也看作点的移动。

相当于$0$可以移动到$1$的位置,而两个$0$相碰会消失。。。

接下来要做的就是把所有的$0$都相碰。

转化问题:移动所有的$0$,让所有的$0$两两相碰。

接下来可以$bfs$,求出每个$0$到其他$0$的最短距离。

再转化问题:有一堆操作,每次操作可以让两个$0$消失,代价为$dis[i][j]$,问让所有的$0$消失的最小代价。

由于$k$非常的小,考虑状压。

方程很好写。

丑陋的代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define Maxn 400050
#define Reg register
#define INF 0x7fffff
#define _min(x,y) ((x)<(y)?(x):(y))
using namespace std;
int n,k,m,cnt,tot,ans,A[Maxn],lss[Maxn],len[Maxn],num[Maxn],dis[250][250];
int dp[1<<17],bin[1<<18];
bool vis[250][Maxn];
int q[Maxn],p[Maxn];
void bfs(int x)
{
    queue<int> q,p;
    q.push(x);
    p.push(0);
    while(!q.empty())
    {
        int k=q.front(); q.pop();
        int l=p.front(); p.pop();
        if(num[k]&&k!=x) dis[num[x]][num[k]]=l;
        vis[num[x]][k]=1;
        for(Reg int i=1;i<=m;++i)
        {
            if(k+len[i]<=n+1&&!vis[num[x]][k+len[i]])
            {
                vis[num[x]][k+len[i]]=1;
                q.push(k+len[i]);
                p.push(l+1);
            }
            if(k-len[i]>=1&&!vis[num[x]][k-len[i]])
            {
                vis[num[x]][k-len[i]]=1;
                q.push(k-len[i]);
                p.push(l+1);
            }
        }
    }
    return;
}
int main()
{
//    freopen("text.in","r",stdin);
//    freopen("text1.out","w",stdout);
    scanf("%d%d%d",&n,&k,&m);
    A[0]=A[n+1]=1;
    memset(dis,0xf,sizeof(dis));
    for(Reg int i=1;i<=n;++i) A[i]=1;
    for(Reg int i=1,x;i<=k;++i) {scanf("%d",&x); A[x]=0;}
    for(Reg int i=0;i<=n;++i) lss[i]=A[i]^A[i+1]^1;
    for(Reg int i=1;i<=n+1;++i) A[i]=lss[i-1];
    for(Reg int i=1;i<=m;++i) scanf("%d",&len[i]);
    for(Reg int i=1;i<=n+1;++i) if(!A[i]) num[i]=++cnt;
    for(Reg int i=1;i<=n+1;++i) if(num[i]) bfs(i);
    for(Reg int i=0;i<=cnt;++i) bin[1<<i]=i;
    memset(dp,0xf,sizeof(dp));
    dp[(1<<cnt)-1]=0;
    ans=INF;
    for(Reg int i=1;i<=cnt;++i)
    {
        for(Reg int k=0;k<(1<<cnt);++k)
        {
            if((k&(1<<(i-1)))==0) continue;
            for(Reg int l=k;l;l-=(l&-l))
            {
                int j=bin[(l&-l)]+1;
                if((l&-l)==0||j==i||j==0) continue;
                dp[k^(1<<(j-1))^(1<<(i-1))]=_min(dp[k^(1<<(j-1))^(1<<(i-1))],dp[k]+_min(dis[j][i],dis[i][j]));
            }
        }
    }
    printf("%d",dp[0]);
    return 0;
}
View Code

总结:

翻看之前的考试总结,好像真的是流水帐。。。

每次考完都感觉很。。没有好好考虑自己的问题。

前半小时读完$T1$的题,然后码了暴力,感觉基本上能拿到$75$分的好成绩。

滚去看$T2$,想到两个思路,一个二分,一个树形$dp$,

二分很快否定,树形$dp$想拿到$k=0,k=1,k=2$的分数。

好像打了不到$2$个小时,手摸样例基本都对。

然后胆战心惊地跳过了这道题,以为自己能拿到$75+60+$的成绩。

$T3$没想拿多少分,因为时间不是很多,之后还要再想$T1$正解。

最近几套题部分分给的很多。

最后$65+40+12=117$。

考场上想不到非常正确的思路,$T2$的$dp$状态都定义错了。。。

最近考试的贪心出现很频繁,如果想到整个题很简单,否则非常麻烦。

$T1$好好想应该能够想出来,不能很快放弃。

好好反思。

原文地址:https://www.cnblogs.com/Milk-Feng/p/11336506.html