FR #11题解

A.

  瞎jb折半dp一下,然后合并一下就好了。

O2加成十分显著。。。6s变成0.9s。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#define maxv 100
#define maxe 20000
using namespace std;
int n,m,d,x,y,z,g[maxv],nume=1,s1,s2,ans=0;
struct edge
{
    int v,f,nxt;
}e[maxe];
bitset <maxv> dp1[maxv][(1<<10)+1][2],dp2[maxv][(1<<10)+1][2];
bitset <1030> vvv[maxv],kr;
bool vis[(1<<20)+1],arr[maxv][15];
void addedge(int u,int v,int f)
{
    e[++nume].v=v;e[nume].f=f;
    e[nume].nxt=g[u];g[u]=nume;
}
void dp1s()
{
    for (register int i=1;i<=s1;i++)
    {
        for (register int j=1;j<=n;j++)
            for (register int k=0;k<(1<<s1);k++)
                dp1[j][k][i&1].reset();
        for (register int j=1;j<=n;j++)
            for (register int k=g[j];k;k=e[k].nxt)
            {
                int v=e[k].v,f=e[k].f,rr=i&1;
                for (register int s=0;s<(1<<(i-1));s++)
                    dp1[j][(s<<1)|f][rr]|=dp1[v][s][rr^1];
                arr[j][i]|=arr[v][i-1];
            }
    }
}
void dp2s()
{
    for (register int i=1;i<=s2;i++)
    {
        for (register int j=1;j<=n;j++)
            for (register int k=0;k<(1<<s2);k++)
                dp2[j][k][i&1].reset();
        for (register int j=1;j<=n;j++)
            for (register int k=g[j];k;k=e[k].nxt)
            {
                int v=e[k].v,f=e[k].f,rr=i&1;
                for (register int s=0;s<(1<<(i-1));s++)
                    dp2[j][(s<<1)|f][rr]|=dp2[v][s][rr^1];
            }
    }
}
void comb()
{
    for (register int i=1;i<=n;i++)
        for (register int j=0;j<(1<<s1);j++)
            if (dp1[i][j][s1&1].count())
                vvv[i][j]=1;
    for (register int i=1;i<=n;i++)
        for (register int j=0;j<(1<<s2);j++)
        {
            if (dp2[i][j][s2&1].count())
            {
                kr.reset();
                for (register int k=1;k<=n;k++)
                    if (dp2[i][j][s2&1][k])
                        kr|=vvv[k];
                for (register int k=0;k<(1<<s1);k++)
                {
                    if (kr[k])
                        vis[k<<s2|j]=true;
                }
            }
        }
}
int main()
{
    scanf("%d%d%d",&n,&m,&d);
    for (register int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z);addedge(y,x,z);
    }
    s1=d/2;s2=d-s1;arr[1][0]=true;dp1[1][0][0][1]=1;
    dp1s();
    for (register int i=1;i<=n;i++)
        if (arr[i][s1]) dp2[i][0][0][i]=1;
    dp2s();
    comb();
    for (register int i=0;i<(1<<d);i++) ans+=vis[i];
    printf("%d\n",ans);
    return 0;
}

B.

   考虑倒着做。这个答案只会单增,于是枚举答案然后check。

怎么check呢?考虑枚举所有这么长的包含这辆车的这一行中的区间,每个位置记下它上面和下面的第一个点(车)的位置,然后相当于区间max,min。

那么单调队列解决。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 2050
#define inf 2000000
using namespace std;
int n,m,k,no[maxn][maxn],so[maxn][maxn],map[maxn][maxn],x[maxn],y[maxn],ans[maxn],dp[maxn][maxn];
int q1[maxn],h1,t1,q2[maxn],h2,t2;
char s[maxn];
void get_table()
{
    for (int j=1;j<=m;j++)
    {
        int ret=0;
        for (int i=1;i<=n;i++)
        {
            no[i][j]=ret;
            if (map[i][j]) ret=i;
        }
        ret=n+1;
        for (int i=n;i>=1;i--)
        {
            so[i][j]=ret;
            if (map[i][j]) ret=i;
        }
    }
}
void dp_()
{
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            if (!map[i][j]) dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
            else dp[i][j]=0;
            ans[k]=max(ans[k],dp[i][j]);
        }
}
bool check(int l,int x,int y)
{
    int lb=-1,rb=inf,ret=0;
    for (int i=y-1;i>=0;i--) if (map[x][i]) {lb=i+1;break;}
    for (int i=y+1;i<=m+1;i++) if (map[x][i]) {rb=i-1;break;}
    int left=max(lb,y-l+1),right=min(y,rb-l+1);h1=h2=1;t1=t2=0;
    for (int i=left;i<=left+l-1;i++)
    {
        while (h1<=t1 && no[x][i]>=no[x][q1[t1]]) t1--;q1[++t1]=i;
        while (h2<=t2 && so[x][i]<=so[x][q2[t2]]) t2--;q2[++t2]=i;
    }
    for (int i=left;i<=right;i++)
    {
        ret=max(ret,so[x][q2[h2]]-no[x][q1[h1]]-1);
        while (h1<=t1 && q1[h1]<=i) h1++;
        while (h1<=t1 && no[x][i+l]>=no[x][q1[t1]]) t1--;q1[++t1]=i+l;
        while (h2<=t2 && q2[h2]<=i) h2++;
        while (h2<=t2 && so[x][i+l]<=so[x][q2[t2]]) t2--;q2[++t2]=i+l;
    }
    return ret>=l;
}
void ask()
{
    for (int i=k;i>=2;i--)
    {
        map[x[i]][y[i]]=0;
        int ret=0;
        for (int j=1;j<=n;j++)
        {
            no[j][y[i]]=ret;
            if (map[j][y[i]]) ret=j;
        }
        ret=n+1;
        for (int j=n;j>=1;j--)
        {
            so[j][y[i]]=ret;
            if (map[j][y[i]]) ret=j;
        }
        int p=ans[i]+1;
        while (check(p,x[i],y[i]) && p<=min(n,m))
            p++;
        ans[i-1]=p-1;
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        for (int j=1;j<=m;j++)
        {
            if (s[j]==‘X‘) map[i][j]=1;
            else map[i][j]=0;
        }
    }
    for (int i=1;i<=n;i++) map[i][0]=map[i][m+1]=1;
    for (int i=1;i<=m;i++) map[0][i]=map[n+1][i]=1;
    for (int i=1;i<=k;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
        map[x[i]][y[i]]=1;
    }
    get_table();
    dp_();
    ask();
    for (int i=1;i<=k;i++) printf("%d\n",ans[i]);
    return 0;
}

C.

  和上一场的C有什么区别吗。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 100500
using namespace std;
long long n,prime[maxn],tot=0,w[maxn][60],ans=0,mn[maxn],f,kr=0;
bool vis[maxn];
void get_table()
{
    for (long long i=2;i<=maxn-500;i++)
    {
        if (!vis[i]) prime[++tot]=mn[i]=i;
        for (long long j=1;i*prime[j]<=maxn-500;j++)
        {
            vis[i*prime[j]]=true;mn[i*prime[j]]=prime[j];
            if (i%prime[j]) continue;break;
        }
    }
    for (long long i=2;i<=maxn-500;i++)
    {
        long long ret=-1,x=i;
        while (x!=1)
        {
            if (mn[x]!=ret) {ret=mn[x];w[i][0]++;w[i][w[i][0]]=mn[x];}
            x/=mn[x];
        }
    }
}
void dfs(long long now,long long top,long long ret,long long val,long long n)
{
    if (now==w[top][0]+1)
    {
        if (!ret) return;
        if (ret&1) kr+=n/val;else kr-=n/val;
        return;
    }
    dfs(now+1,top,ret,val,n);
    dfs(now+1,top,ret+1,val*w[top][now],n);
}
void ask(long long n,long long val) {kr=0;dfs(1,val,0,1,n);}
int main()
{
    scanf("%lld",&n);
    get_table();
    long long top=sqrt(n);
    for (long long i=2;i<=top;i++)
    {
        ask(n/i,i);ans+=(n/i)-kr;
        ask(i,i);ans-=i-kr;
    }
    printf("%lld\n",ans);
    return 0;
}
时间: 2024-12-26 02:27:41

FR #11题解的相关文章

2019.11.11 题解报告

目录 2019.11.11 题解报告 答题情况: 各题目分析: 题目解析: 代码实现: 2019.11.11 题解报告 \[N^2\text{狂草1e5它不香嘛?}\] \[\text{By:Unluckierblock}\] 答题情况: 总成绩 : 169, 排名: 11 / 32 T1 : 0 T2 : 99 T3 : 70 各题目分析: 题目 1 : 预估成绩 : 60 实际成绩 : 0 考试用时 : 8 : 00 ~ 8 : 50 , 9 : 50 ~ 10 : 10 没有什么感觉 ,

BestCoder Round #11 题解集合

1001.Alice and Bob 签到题*1,只要x * 2 == n && y * 2 == m就满足条件. 1 var 2 m, n, x, y : int64; 3 4 begin 5 while not eof do begin 6 readln(m, n, x, y); 7 if (m = 2 * x) and (n = 2 * y) then writeln('YES') else writeln('NO'); 8 end; 9 end. 1002.Bob and math

LeetCode(11)题解: Container With Most Water

https://leetcode.com/problems/container-with-most-water/ 题目: Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0)

模拟11题解

T1[A. string]「桶排序」「线段树」 线段树维护区间的每个字母出现了多少次, 在排序的时候,先查询一个区间的每个字母的出现次数,然后挨个区间赋值 复杂度  $O(mlog(n)*26)$ 优化常数(26):定义f(懒标记):f!=0时,代表子树都被赋值为了同一个值;f==0,表示不相等. 将区间查询改为只有访问到了f!=0再对ans贡献return,pushup,pushdown同理,避免了枚举26 当然也可以循环展开勉强卡过 1 #include<iostream> 2 #incl

FR #10题解

好 蠢 啊 A. 标准分治.每次从分治区间中找到最大值的位置m,设f[l,r]为[l,r]的答案,那么f[l,r]=f[l,m-1]+f[m+1,r]+跨过m点的贡献. 然后枚举小的区间放到大的区间中查就行了.复杂度nlog^2n. TM的这5e5你给128M怎么回事...开6s又怎么回事... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define

FR #1题解

A. 建图跑最小费用最大流.分类讨论每种情况如何连边,费用怎么定. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define maxv 105 #define maxe 100500 #define inf 1000000000 using namespace std; int n,m,a[maxv],b[ma

FR #12题解

A. 我的做法是nmlogn的....直接做m次堆贪心就可以.按理说是能过的... 正解直接是在原dp上搞一搞...可以做到n^2+nlog? 2333 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define maxn 2050 using namespace std; long long n,m,a[max

FR #2题解

A. 考虑把(u,v)的询问离线挂在u上,然后dfs,每次从fath[x]到[x]相当于x子树dis区间加1,x子树以外区间-1,然后维护区间和区间平方和等. 常数略大. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define maxv 100500 #define maxe 200500 using na

模拟11 题解

A. string 类似 HEOI2016排序 . 排序这道题因为只询问单点最终答案,二分答案, 将小于和大于等于答案的数分别设为0 1, 用线段树维护0 1的排序即可. 算法一: 本题中的1-n变成了0-25(即a-z),单点询问变成了全体询问. 仿照排序那道题的做法,线段树优化桶排序. 维护每个节点每个字符的cnt值,区间查询后区间赋值即可. 然而up和down函数都是$O(26)$的,加上修改操作最多被拆分为26个, 总的复杂度为$O(nlogn26^2)$. 需要卡常. 考场上码出了80