AtCoder Regular Contest 099

C - Minimization
最开始只有一个1,反正无论怎样我都可以使得k-1个变为1。

排列是什么样子不重要,答案就是(n-1)/k+((n-1)%k?1:0)。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,k,a[N];
int main()
{
    
    
    scanf("%d%d",&n,&k);k--;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    printf("%d\n",(n-1)/k+((n-1)%k?1:0));
}

D - Snuke Numbers
直接思考不好思考,考虑采用打表观察法,我们对n/s(n)进行打表。

可以发现,这个序列是下降到一定程度后,然后增大,再下降。

发现是每10个有一次上升,如果i/s(i)<=(i+1)/s(i+1),我们称i是个上升点。

也就是它的下一个的值会上升到比它大,否则称它是个下降点。

那么现在发现每10个就是一个上升点(1~9比较特殊),那么这些上升点都是答案吗?

我们再对每10个的上升点进行打表,可以发现和每1个点打个表的类似。

这里也每10个点出现一个上升点,那么这里10个点就相当于100个点了。

从这样推下来,从一开始,每1个点是答案,每10个点是答案,每1000个点是答案。。。每t个点是答案,发现这个t只升不降并且是10的幂次。

那么我们只要暴力求就行了,从第一个答案ans=1,t=1开始,判断ans+t是不是个上升点。

如果是则ans=ans+t,否则将t=t*10,再ans=ans+t。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int sum(ll x)
{
    
    
    int ans=0;
    while(x)
        ans+=x%10,x/=10;
    return ans;
}
int main()
{
    
    
    int k;scanf("%d",&k);
    ll ans=1,add=1;
    while(k--)
    {
    
    
        printf("%lld\n",ans);
        ll s1=ans+add,s2=ans+2*add;
        if(s1*sum(s2)<=s2*sum(s1)) ans=s1;
        else add*=10,ans+=add,assert(ans*sum(ans+add)<=(ans+add)*sum(ans));
    }
    //for(int i=1;i<=1000;i++)
    //    if(1.0*i/sum(i)<1.0*(i+1)/sum(i+1))
    //    cout<<i<<' '<<1.0*i/sum(i)<<endl;
}
/*
9 1
19 1.9
29 2.63636
39 3.25
49 3.76923
59 4.21429
69 4.6
79 4.9375
89 5.23529
99 5.5
109 10.9

119 10.8182
129 10.75
139 10.6923
149 10.6429
159 10.6
169 10.5625
179 10.5294
189 10.5
199 10.4737

209 19
219 18.25
229 17.6154
239 17.0714
249 16.6
259 16.1875
269 15.8235
279 15.5
289 15.2105
299 14.95

309 25.75
319 24.5385
329 23.5
339 22.6
349 21.8125
359 21.1176
369 20.5
379 19.9474
389 19.45
399 19

409 31.4615
419 29.9286
429 28.6
439 27.4375
449 26.4118
459 25.5
469 24.6842
479 23.95
489 23.2857
499 22.6818
509 36.3571
519 34.6
529 33.0625
539 31.7059
549 30.5
559 29.4211
569 28.45
579 27.5714
589 26.7727
599 26.0435
609 40.6
619 38.6875
629 37
639 35.5
649 34.1579
659 32.95
669 31.8571
679 30.8636
689 29.9565
699 29.125
709 44.3125
719 42.2941
729 40.5
739 38.8947
749 37.45
759 36.1429
769 34.9545
779 33.8696
789 32.875
799 31.96
809 47.5882
819 45.5
829 43.6316
839 41.95
849 40.4286
859 39.0455
869 37.7826
879 36.625
889 35.56
899 34.5769
909 50.5
919 48.3684
929 46.45
939 44.7143
949 43.1364
959 41.6957
969 40.375
979 39.16
989 38.0385
999 37
*/

E Independence
这题如果在原图上思考,那就直接凉凉了。

考虑转换成补图,那么补图中有边的点不能在一个集合内,考虑给补图中每个连通分量染色

那么一个点染色,其它的点的颜色就确定了。

然后可以在不同连通分量的染了色的点进行组合,就转换成了背包问题。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=705,low=700;
int n,m,sum,c[N];
bool vis[N][N];
void dfs(int u,int x)
{
    
    
    c[u]=x;
    if(x==1) sum++;
    else sum--;
    for(int i=1;i<=n;i++)
        if(vis[u][i])
    {
    
    
        if(c[i])
        {
    
    
            if(c[i]==c[u])
            {
    
    
                printf("-1\n");exit(0);
            }
        }
        else dfs(i,c[u]==1?2:1);
    }
}
vector<int>v;
bool dp[N<<1],f[N<<1];
int main()
{
    
    
    memset(vis,true,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) vis[i][i]=false;
    for(int i=1;i<=m;i++)
    {
    
    
        int u,v;scanf("%d%d",&u,&v);
        vis[u][v]=vis[v][u]=false;
    }
    for(int i=1;i<=n;i++)
        if(!c[i]) sum=0,dfs(i,1),v.push_back(sum);
    dp[0+low]=true;
    for(int i=0;i<v.size();i++)
    {
    
    
        memset(f,false,sizeof(f));
        for(int j=-n;j<=n;j++)
            if(dp[j+low])
        {
    
    
            f[j+low-v[i]]=true;
            f[j+low+v[i]]=true;
        }
        for(int j=-n;j<=n;j++)
            dp[j+low]=f[j+low];
    }
    int ans=inf;
    for(int i=-n;i<=n;i++)
        if(dp[i+low])
        {
    
    
            int a=(n+i)/2,b=n-a;
            if(a==0||b==0) continue;
            ans=min(ans,a*(a-1)/2+b*(b-1)/2);
        }
    if(ans==inf) ans=-1;
    printf("%d\n",ans);
}

F Eating Symbols Hard
考虑哈希,设哈希前缀分别为f1,f2…fn,那么从1到走到n得到的答案就是fn。

首先把所有的hs前缀都加入map,可以得到等于fn的前缀的个数,然后考虑一个前缀fi

如果fi+1~fn中某个前缀减去fn等于fi,那么就说明有一段以i+1开头的区间满足条件。

考虑后面满足条件的前缀的哈希值应为fi+fn*si,其中si为阶数,为啥呢要乘个阶数呢?

如果后面某段i+1,j的哈希值等于fn,但是由于只记录了前缀。

所以i+1,j放在了前缀里面,相对于第i个前缀,它的阶数被乘了个si。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5,mod1=998244353,mod2=1000000007,mod3=2333;
int n,x=799633,invx1,invx2,invx3;
ll sum1=1,sum2=1,sum3=1;
ll s1[N],s2[N],s3[N];
char s[N];
ll inv(ll x,ll mod){
    
    return x==1?x:(mod-mod/x)*inv(mod%x,mod)%mod;}
struct node
{
    
    
    ll s1,s2,s3;
    node(ll s1=0,ll s2=0,ll s3=0):s1(s1),s2(s2),s3(s3){
    
    }
    void add()
    {
    
     s1=(s1+sum1)%mod1;s2=(s2+sum2)%mod2;s3=(s3+sum3)%mod3;}
    void sub()
    {
    
     s1=(s1+mod1-sum1)%mod1;s2=(s2+mod2-sum2)%mod2;s3=(s3+mod3-sum3)%mod3;}
    bool operator<(const node&o)const
    {
    
    
        if(s1==o.s1&&s2==o.s2) return s3<o.s3;
        if(s1==o.s1) return s2<o.s2;
        return s1<o.s1;
    }
};
void div()
{
    
    
    sum1=sum1*invx1%mod1;
    sum2=sum2*invx2%mod2;
    sum3=sum3*invx3%mod3;
}
void mul()
{
    
    
    sum1=sum1*x%mod1;
    sum2=sum2*x%mod2;
    sum3=sum3*x%mod3;
}
node f[N];
map<node,int>mp;
int main()
{
    
    
    invx1=inv(x%mod1,mod1);
    invx2=inv(x%mod2,mod2);
    invx3=inv(x%mod3,mod3);
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
    
    
        f[i]=f[i-1];
        if(s[i]=='>') div();
        if(s[i]=='<') mul();
        if(s[i]=='+') f[i].add();
        if(s[i]=='-') f[i].sub();
        mp[f[i]]++;
    }
    sum1=sum2=sum3=1;
    node now;
    ll ans=mp[f[n]];
    for(int i=1;i<=n;i++)
    {
    
    
        mp[f[i]]--;
        if(s[i]=='>') div();
        if(s[i]=='<') mul();
        if(s[i]=='+') now.add();
        if(s[i]=='-') now.sub();
        node p=now;
        p.s1=(p.s1+f[n].s1*sum1)%mod1;
        p.s2=(p.s2+f[n].s2*sum2)%mod2;
        p.s3=(p.s3+f[n].s3*sum3)%mod3;
        ans+=mp[p];
    }
    printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/Huah_2018/article/details/104236592