codeforces选做

收录了最近本人完成的一部分codeforces习题,不定期更新

codeforces 1132E Knapsack

注意到如果只使用某一种物品,那么这八种物品可以达到的最小相同重量为\(840\)

故答案一定可以被写成\(840k+x(k,x\in N_+)\),我们将\(x\)称为”余下的部分”

故而设\(dp[i][j]\)为当前考虑了前\(i\)个物品,它们所占的余下的部分的重量为\(j\)时,最多可以组成多少个\(840\)

对于每个\(i\)预处理出枚举上界暴力转移即可

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x)&(-x)
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=a;i>=b;i--)
#define maxd 1000000007
typedef long long ll;
const int N=100000;
const double pi=acos(-1.0);
ll w,a[10],ans=0,dp[9][100100];

ll read()
{
    ll x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

int main()
{
    w=read();
    rep(i,1,8) a[i]=read();
    ll maxp=8*840;
    memset(dp,-1,sizeof(dp));
    dp[0][0]=0;
    rep(i,1,8)
    {
        rep(j,0,maxp)//余下的重量 
        {
            if (dp[i-1][j]==-1) continue;
            ll k=min(1ll*840/i,a[i]);
            rep(p,0,k)//当前有多少作为余下的部分 
            {
                dp[i][j+p*i]=max(dp[i][j+p*i],dp[i-1][j]+(a[i]-p)/(840/i));
            }
        }
    }
    ll ans=0;
    rep(i,0,min(w,maxp))
    {
        if (dp[8][i]==-1) continue;
        ans=max(ans,i+min((w-i)/840,dp[8][i])*840);
    }
    printf("%lld",ans);
    return 0;
} 

codeforces1154G Minimum Possible LCM

根据\(lcm(x,y)=\frac{xy}{gcd(x,y)}\)进行计算

枚举约数\(d\),每次找到满足\(d|x\)的最小的两个\(x\),用它们更新答案即可

为什么这样做可行?我们假设满足\(d|x​\)的数从小到大一次为\(a_1,a_2,\cdots,a_k​\)

不妨对\(a_1,a_2,a_k(k>2)\)这三个数进行分析,并且我们保证\(gcd(a_1,a_k)=d\),否则我们可以在枚举更大的\(d\)的时候考虑到这一组

  • \(gcd(a_1,a_2)=d\),那么一定有\(\frac{a_1a_2}{gcd(a_1,a_2)}<\frac{a_1a_k}{gcd(a_1,a_k)}\)
  • \(gcd(a_1,a_2)>d​\),则\(\frac{a_1a_2}{gcd(a_1,a_2)}<\frac{a_1a_2}{d}<\frac{a_1a_k}{d}​\)

证毕

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x)&(-x)
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=a;i>=b;i--)
typedef long long ll;
const int N=100000;
const double pi=acos(-1.0);
int n,a[1001000],cnt[10010000];
vector<int> ans;

int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

ll gcd(ll x,ll y)
{
    if (!y) return x;else return gcd(y,x%y);
}

int main()
{
    n=read();ll maxd=0;
    rep(i,1,n) {a[i]=read();cnt[a[i]]++;maxd=max(maxd,1ll*a[i]);}
    ll lcm=(ll)1e18+7,val1,val2;
    rep(i,1,maxd)
    {
        int j;ans.clear();
        for (j=i;j<=maxd;j+=i)
        {
            if (!cnt[j]) continue;
            int tmp=cnt[j];
            while ((tmp) && (ans.size()<2))
            {
                ans.push_back(j);
                tmp--;
            }
            if (ans.size()==2) break;
        }
        if (ans.size()!=2) continue;
        ll now=1ll*ans[0]*ans[1]/gcd(ans[0],ans[1]);
        if (now<lcm)
        {
            lcm=now;val1=ans[0];val2=ans[1];
        }
    }
    ll pos1=0,pos2=0;
    rep(i,1,n)
    {
        if ((!pos1) && (a[i]==val1)) pos1=i;
        else if ((!pos2) && (a[i]==val2)) pos2=i;
    }
    if (pos1>pos2) swap(pos1,pos2);
    printf("%lld %lld",pos1,pos2);
    return 0;
}

codeforces 1120D Power Tree

如果不要输出方案的话那就可以大力\(dp\),记\(dp[u][0/1]\)为控制以\(u\)为根的子树的最小代价,其中\(0\)表示不选\(u\)的祖先\(1\)表示选,考虑\(u\)的儿子是不需要选祖先或者某一个需要祖先来进行转移

然而似乎输出方案很难写。。。弃了弃了看题解

将控制一个点的操作转化到树的dfs序上,也就是控制了一段区间,注意到这个\(dfs\)序我们只需要保留叶子结点

为了转化区间操作,我们将这个\(dfs\)转化成差分序列,即一次对\([l,r]\)的操作可以看做是在\(l\)加上一个数同时在\(r+1\)上减去一个数

考虑题目是要求最后能使得整个序列都变成\(0\),等价于让这个差分序列变成\(0\)

也就是说对于每个点我们都希望有一条能单独修改它的路径

将差分序列的每个位置看成是一个点,一次修改看成是一条边,跑kruskal即可

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x)&(-x)
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=a;i>=b;i--)
#define maxd 1000000007
typedef long long ll;
const int N=100000;
const double pi=acos(-1.0);
struct sqnode{
    int to,nxt;
}sq[400400];

struct edgenode{
    int u,v,w,id;
}edge[200200];
bool operator <(const edgenode &p,const edgenode &q)
{
    return p.w<q.w;
}
int n,w[200200],all=0,head[200200],l[200200],r[200200],tot=0,tim=0,fa[200200];
bool vis[200200];

int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

int find(int x)
{
    if (fa[x]==x) return x;
    fa[x]=find(fa[x]);
    return fa[x];
}

void dfs(int u,int fu)
{
    int i;
    l[u]=maxd;r[u]=0;
    for (i=head[u];i;i=sq[i].nxt)
    {
        int v=sq[i].to;
        if (v==fu) continue;
        dfs(v,u);vis[u]=1;
        l[u]=min(l[u],l[v]);
        r[u]=max(r[u],r[v]);
    }
    if ((!vis[u]) && (u!=1)) {l[u]=(++tim);r[u]=tim;}
    edge[++tot]=(edgenode){l[u],r[u]+1,w[u],u};
}

void add(int u,int v)
{
    all++;sq[all].to=v;sq[all].nxt=head[u];head[u]=all;
}
        

int main()
{
    n=read();
    rep(i,1,n) w[i]=read();
    rep(i,1,n-1)
    {
        int u=read(),v=read();
        add(u,v);add(v,u);
    }
    memset(vis,0,sizeof(vis));
    dfs(1,0);
    sort(edge+1,edge+1+n);
    ll ans=0;
    rep(i,1,tim+1) fa[i]=i;
    memset(vis,0,sizeof(vis));
    int l=1,r=1;
    for (l=1;l<=n;l=r+1)
    {
        while ((r<n) && (edge[r+1].w==edge[l].w)) r++;
        rep(j,l,r)
        {
            int x=edge[j].u,y=edge[j].v,
                fx=find(x),fy=find(y);
            if (fx!=fy) vis[edge[j].id]=1;
        }
        rep(j,l,r)
        {
            int x=edge[j].u,y=edge[j].v,
                fx=find(x),fy=find(y);
            if (fx!=fy) {fa[fx]=fy;ans+=edge[j].w;}
        }
    }
    int cnt=0;
    rep(i,1,n) if (vis[i]) cnt++;
    printf("%lld %d\n",ans,cnt);
    rep(i,1,n) if (vis[i]) printf("%d ",i);
    return 0;
} 

codeforces1137D Cooperative Game

学不来告辞

利用floyd和pollard-rho中的判圈方式,我们让\(0\)号棋子一次走一步,\(1\)号棋子两次走一步,直到两者相遇

我们假设此时\(1\)号棋子走了\(T+x\)步,那么\(0\)号棋子走了\(2(T+x)\)

且应有\(T+x\equiv 0(mod\ C)\)

因此\(0\)\(1\)号这两颗棋子再走\(T\)步即可到达终点

且剩下的\(8\)棵棋子需要走一条链的长度,也是\(T\)

于是我们可以在\(2x+3T\)的步数内完成这一过程,由于\(x<C\),所以总步数小于\(3(T+C)\)

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x)&(-x)
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,a,b) for (int i=a;i>=b;i--)
#define maxd 1000000007
typedef long long ll;
const int N=100000;
const double pi=acos(-1.0);
char s[20];

int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

int get_num()
{
    int ans=read();
    rep(i,1,ans) scanf("%s",s);
    return ans;
}

int main()
{
    while (1)
    {
        printf("next 0\n");fflush(stdout);
        int cnt=get_num();
        printf("next 0 1\n");fflush(stdout);
        cnt=get_num();
        if (cnt==2) break;
    }
    while (1)
    {
        printf("next 0 1 2 3 4 5 6 7 8 9\n");fflush(stdout);
        int cnt=get_num();
        if (cnt==1) break;
    }
    printf("done\n");fflush(stdout);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zhou2003/p/10739708.html