2018.08.10【省赛&提高A组模拟】比赛总结

题解

这次题目可真是太难了!
糟糕的运气使我AK的步伐寸步难行(士气严重下降)。
1735779-20190709223914887-760612058.gif

T1

这题还是比较水的(尽管我比赛时只拿了50分)
一些大佬们说:这题只是一道简单的暴力题,枚举l+二分r 就可以了。
另一些DL们说:这题就是一道水DP,用SPFA/Dij做就可以了。
我说:这题是一道看不出正解的难题,只能暴力水30分了!
于是乎我打了一个暴力,却手残脑残打了一个贪心,使当前的 L 尽可能小
本以为会GG,结果居然拿了50分!
可以发现,结果区间的L和R都必定在输入里出现(不然怎么可能走不过去?)
因此枚举L,二分R即可!

T2

这题还是有点水的(尽管我爆0)
不难发现这题要用Trie
可以按照T建也可以按照S建,我是按照T建的。
每一个节点上都额外多储存两个变量——last和sum,last建树时最后经过该节点的字符串编号,sum表示到当前字符串,与该字符串前缀相同的字符串组成的A数组中的最长全0串。
建树时,要维护每一个节点的last和sum。
询问时,走到S的尽头节点x(若走不到则输出n),输出\(\max(sum[x],n-last[x])\)即可。

T3

这题十分毒瘤!
一看题目,就觉得正解就是数论。
可以发现,假如 x[p]能够求出来,那么必定存在一对(i,j),满足 \(P\mod k[i]=0,P\mod k[j]=1\)
这相当于 \(p=a1*k[i]=a2*k[j]+1\)\(a1*k[i]-a2*k[j]=1\),这个就可以用扩展欧几里得算法来求通解。
注意:有解的条件必定是gcd(k[i],k[j])=1
但是这样会重复,因此要用容斥原理去重。
我们可以设 f[s1][s2](其中 s1 表示 p mod k[i]=0 的集合,s2 表示 p mod k[j]=1 的集合,s1,s2 可以用二进制来压),来表示满足以下方程的 p 的个数:
\(P\mod k[i1]=0,P\mod k[i2]=0,P\mod k[i3]=0\), ……
\(P\mod k[j1]=1,P\mod k[j2]=1,P\mod k[j3]=1\), ……
事实上,这等价于满足两个方程。
\(P\mod lcm(k[i1],k[i2],…)=0,P\mod lcm(k[j1],k[j2],…)=1\)
这个方程的解的个数可以用扩展欧几里得来求。
因此我们可以用一个DFS枚举s1和s2来实现,当一共选择了奇数个元素时就为重复要减去,否则为不重复要加上。


CODE

T1

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 1010
#define M 3010
#define inf 1e+6
struct EDGE
{
    int end,l,r,next;
}edge[M<<1];
struct node
{
    int first,num,l,r;
    node(){first=num=l=r=0;}
}a[N];
int n,m,s,ans,L,R,left[M],right[M];
bool b[N];
inline void inc(int x,int y,int l,int r)
{
    edge[++s]=(EDGE){y,l,r,a[x].first};
    a[x].first=s;
    edge[++s]=(EDGE){x,l,r,a[y].first};
    a[y].first=s;
}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
bool pd(int k)
{
    b[k]=0;
    if(k==n) return 1;
    for(int i=a[k].first;i;i=edge[i].next)
        if(edge[i].l<=L&&edge[i].r>=R&&b[edge[i].end]&&pd(edge[i].end))
            return 1;
    return 0;
}
int main()
{
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    int i,j,x,y,l,r,mid,ans=0,ansl;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&x,&y,&left[i],&right[i]);
        inc(x,y,left[i],right[i]);
        left[i]=-left[i];
    }
    sort(left+1,left+m+1);
    sort(right+1,right+m+1);
    for(i=1;i<=m;i++) if(left[i]!=left[i-1])
    {
        l=1,r=m;
        while(l<=r)
        {
            mid=(l+r)/2;
            L=-left[i],R=right[mid];
            if(R-L+1<ans) l=mid+1;
            else 
            {
                for(j=1;j<=n;j++) b[j]=1;
                if(pd(1))
                {
                    if(R-L+1>=ans)
                        ans=R-L+1,ansl=L;
                    l=mid+1;
                }
                else r=mid-1;
            }
        }
    }
    printf("%d\n",ans);
    for(i=ansl;i<ans+ansl;i++) printf("%d ",i);
    return 0;
}

T2

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define M 5000010
#define minus 'a'
struct trie
{
    int last,ans;
    trie *son[3];
    trie()
    {
        last=ans=0;
        son[0]=son[1]=son[2]=NULL;
    }
};
trie *root=new trie;
char st[M];
int n,m,len,num;
inline int max(int x,int y){return x>y?x:y;}
inline void insert(trie *p)
{
    len=strlen(st+1);
    int i,k;
    for(i=1;i<=len;i++)
    {
        k=st[i]-minus;
        if(p->son[k]==NULL)
        {
            p->son[k]=new trie;
            p=p->son[k];
        }
        else
        {
            p=p->son[k];
            p->ans=max(p->ans,num-(p->last)-1);
        }
        p->last=num;
    }
}
inline int find()
{
    int i;
    trie *p=root;
    len=strlen(st+1);
    for(i=1;i<=len;i++)
    {
        if(p->son[st[i]-minus]) p=p->son[st[i]-minus];
        else return n;
    }
    return max(p->ans,n-p->last);
}
int main()
{
    freopen("word.in","r",stdin);
    freopen("word.out","w",stdout);
    int i;trie *p;
    scanf("%d%d",&n,&m);
    for(num=1;num<=n;num++)
    {
        scanf("%s",st+1);
        insert(root);
    }
    while(m--)
    {
        scanf("%s",st+1);
        printf("%d\n",find());
    }
    return 0;
}

T3

#include<cstdio>
using namespace std;
#define ll long long
ll n,m,ans,x,y,a[12];
ll exgcd(ll a,ll b)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    ll d=exgcd(b,a%b),t=x;
    x=y,y=t-a/b*y;
    return d;
}
inline ll gcd(ll x,ll y)
{
    if(y==0) return x;
    return gcd(y,x%y);
}
inline ll lcm(ll x,ll y)
{
    if(x==0) return y;
    if(y==0) return x;
    return x/gcd(x,y)*y;
}
void dfs(ll k,ll lcm1,ll lcm2,ll sum)
{
    if(lcm1>n||lcm2>n) return;
    if(k>m)
    {
        if(lcm1==0||lcm2==0) return;
        if(exgcd(lcm1,lcm2)>1) return;
        x=(x%lcm2+lcm2)%lcm2;
        ans+=(n/lcm1-x+lcm2)/lcm2*(sum&1?-1:1);
        return;
    }
    dfs(k+1,lcm1,lcm2,sum);
    dfs(k+1,lcm(lcm1,a[k]),lcm2,sum+1);
    dfs(k+1,lcm1,lcm(lcm2,a[k]),sum+1);
}
int main()
{
    freopen("sazetak.in","r",stdin);
    freopen("sazetak.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=m;i++) scanf("%lld",&a[i]);
    a[++m]=n;
    dfs(1,0,0,0);
    printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/huangzihaoal/p/11160977.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值