AGC 012

A
贪心,最小的n个数肯定分在每个组用来垫,然后从小到大考虑,对于每个数肯定尽量让他成为某组最大的,这样后面比他大的数就有更多成为次大的,肯定比让他成为次大的更优,所以答案就是排序后第n+1,n+3,n+5…3n-1项的和

B
倒着处理询问,变成每次将距离点v<=d的所有点中无色的点染色,对每个点记录v[x][d]表示离这个点距离<=d的点是否都被染色,复杂度 O(nd)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 110000;
const int maxk = 12;

int n,m,Q;
int col[maxn];
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}

int e[maxn][3];
bool v[maxn][maxk];
struct node{int x,p;};
queue<node>q;

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y; scanf("%d%d",&x,&y);
        ins(x,y); ins(y,x);
    }
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++) scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);

    for(int i=Q;i>=1;i--)
    {
        int cc=e[i][2];
        q.push((node){e[i][0],e[i][1]});
        while(!q.empty())
        {
            const node now=q.front(); q.pop();
            if(v[now.x][now.p]) continue;
            if(!col[now.x]) col[now.x]=cc;
            for(int j=now.p;j>=0&&!v[now.x][j];j--) v[now.x][j]=true;
            if(now.p==0) continue;
            for(int k=fir[now.x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
                q.push((node){y,now.p-1});
        }
    }
    for(int i=1;i<=n;i++) printf("%d\n",col[i]);

    return 0;
}

C
蜜汁构造
考虑一个1~k的排列+1~k的序列,他的形如TT的子序列就是该排列上升子的数量
将空集也定义成上升子后,在一个1~k的排列后加k+1,其上升子数量*2
在其前面加上k+1,上升子数量+1
就可以倍增得到N+1个上升子的1~k的排列了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 210;

ll n;
int t[maxn],tp;
int q[maxn],head,tail;

int main()
{
    scanf("%lld",&n); ++n;
    while(n) t[++tp]=n&1,n>>=1;
    int k=1; q[head=tail=100]=k;
    for(int i=tp-1;i>=1;i--)
    {
        if(t[i]) q[--head]=++k;
        if(i>1) q[++tail]=++k;
    }
    printf("%d\n",(tail-head+1)*2);
    for(int i=head;i<=tail;i++) printf("%d ",q[i]);
    for(int i=1;i<=k;i++) printf("%d ",i);

    return 0;
}

D
对于同色的球,排序后,若wi(i>1)+w1<=X,则wi可以视为w1(和他换相当于和w1换)
对于不同色的球,找出所有颜色的球的w1最小和次小的2种颜色c1,c2
对于除了c1的其他颜色,这些球若 +wc1[1]<=Y ,则他们也是可以自由活动的,否则只能固定在原位不动,相当于没有
对于c1色的球,除了用 wc1[1] 同色更新,要用 wc2[1] 异色尝试更新自由活动的球集合,找出所有自由活动的球后,就是一个有相同元素的排列数

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 210000;
const ll Mod = 1e9+7;

ll inv[maxn],s[maxn];
void pre()
{
    s[0]=1ll; for(int i=1;i<maxn;i++) s[i]=s[i-1]*(ll)i%Mod;
    inv[0]=inv[1]=1;
    for(ll i=2;i<maxn;i++)
        inv[i]=(-(Mod/i)*inv[Mod%i]%Mod+Mod)%Mod;
    for(int i=1;i<maxn;i++) inv[i]=inv[i]*inv[i-1]%Mod;
}

int n,X,Y;
vector<int>V[maxn];

int main()
{
    pre();
    scanf("%d%d%d",&n,&X,&Y);
    for(int i=1;i<=n;i++)
    {
        int w,c; scanf("%d%d",&c,&w);
        V[c].push_back(w);
    }
    for(int i=1;i<=n;i++) sort(V[i].begin(),V[i].end());
    int m1=0,m2=0;
    for(int i=1;i<=n;i++) if(V[i].size())
    {
        for(int j=1;j<V[i].size();j++) if(V[i][j]+V[i][0]<=X) V[i][j]=V[i][0];
        if(!m1||V[m1][0]>V[i][0]) m2=m1,m1=i;
        else if(!m2||V[m2][0]>V[i][0]) m2=i;
    }
    if(!m2) return puts("1"),0;

    int ren=1; ll ans=1ll;
    for(int j=1;j<V[m1].size();j++)
        if(V[m1][j]+V[m1][0]<=X||V[m1][j]+V[m2][0]<=Y) ren++;
    ans=ans*inv[ren]%Mod;
    for(int i=1;i<=n;i++) if(i!=m1)
    {
        int now=0;
        for(int j=0;j<V[i].size();j++) if(V[m1][0]+V[i][j]<=Y) now++;
        ans=ans*inv[now]%Mod;
        ren+=now;
    }
    ans=ans*s[ren]%Mod;
    printf("%lld\n",ans);

    return 0;
}

E
若骆驼不跳跃,则他肯定往两边遍历所有他能走到的点,将1~n划分为若干个内部互相可达的区间
注意到骆驼最多跳跃logV次,相当于有logV层,每层有若干条不相交、覆盖该层的线段,然后对于最大的一层d,询问对于每个线段,在0~d-1层每层挑一个线段,加上这个线段是否能覆盖1~n
因为d=logV很小,可以状压f[i]表示i状态的这些层最多覆盖1~f[i],g[i]表示最多从n覆盖到n~g[i],再做一个整合的dp

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void up(int &x,const int &y){if(x<y)x=y;}
inline void down(int &x,const int &y){if(x>y)x=y;}
const int maxn = 210000;
const int maxd = 20;
const int maxv = 810000;

int n,V,d,t[maxn];
int p[maxn];
int li[maxn*maxd],ri[maxn*maxd],cnt,bel[maxd][maxn];
int f[maxv],g[maxv];
int dp[maxn];

int main()
{
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++) scanf("%d",&p[i]);
    d=0; for(int now=V;now;now>>=1) t[++d]=now; t[++d]=0;
    for(int i=1;i*2<=d;i++) swap(t[i],t[d-i+1]);

    for(int i=1;i<=d;i++)
    {
        int cc=t[i]; int now=cnt;
        bel[i][1]=++cnt;
        li[cnt]=ri[cnt]=1;
        for(int j=2;j<=n;j++)
        {
            if(p[j]-p[j-1]<=cc) ri[bel[i][j]=bel[i][j-1]]=j;
            else li[bel[i][j]=++cnt]=ri[cnt]=j;
        }
    }
    int al=1<<d-1;
    memset(f,-1,sizeof f); f[0]=0;
    for(int i=0;i<al;i++) if(f[i]!=-1)
    {
        if(f[i]==n) 
        {
            for(int j=0;j<d-1;j++) if(!(i>>j&1)) 
                up(f[i+(1<<j)],f[i]);
        }
        else
        {
            for(int j=0;j<d-1;j++) if(!(i>>j&1)) 
                up(f[i+(1<<j)],ri[bel[j+1][f[i]+1]]);
        }
    }
    for(int i=0;i<al;i++) g[i]=n+1;
    for(int i=0;i<al;i++)
    {
        if(g[i]==1)
        {
            for(int j=0;j<d-1;j++) if(!(i>>j&1))
                down(g[i+(1<<j)],g[i]);
        }
        else
        {
            for(int j=0;j<d-1;j++) if(!(i>>j&1))
                down(g[i+(1<<j)],li[bel[j+1][g[i]-1]]);
        }
    }
    for(int i=0;i<=n;i++) dp[i]=n+1;
    for(int i=0;i<al;i++) down(dp[f[i]],g[al-1-i]);
    for(int i=n-1;i>=0;i--) down(dp[i],dp[i+1]);

    for(int i=1;i<=n;i++)
    {
        int l=li[bel[d][i]],r=ri[bel[d][i]];
        if((r==n&&f[al-1]>=l-1)||(r<n&&dp[l-1]<=r+1)) puts("Possible");
        else puts("Impossible");
    }

    return 0;
}

F
主要证明2个充要性质:
b[i]在a[i]~a[n-i+1];
对于任意j< i,不存在b[j]在b[i]和b[i+1]之间
(我其实还是有点晕qwq就不在这里证了)

然后可以倒着dp,f[i][j][k]表示dp到n-i+1轮,可选的数有j个,这一轮选的数在其中排第k的方案数,枚举上一轮b[i]在其中的排名,由于性质,中间夹的数要删掉,不可能在之前的b[j]出现

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 110;
const int Mod = 1e9+7;

int n,m;
int a[maxn];
int f[maxn][maxn][maxn];

void add(int &x,int y){ x+=y; if(x>=Mod)x-=Mod; }

int main()
{
    scanf("%d",&n); m=2*n-1;
    for(int i=1;i<=m;i++) scanf("%d",&a[i]);
    sort(a+1,a+m+1);

    f[1][1][1]=1;
    for(int k=1;k<n;k++)
    {
        for(int i=1;i<=m;i++) for(int j=1;j<=i;j++) if(f[k][i][j])
        {
            int tmp=f[k][i][j];
            int now=j,al=i;
            if(a[n-k]!=a[n-k+1]) al++,now++;
            if(a[n+k]!=a[n+k-1]) al++;

            add(f[k+1][al][now],tmp);
            for(int l=1;l<now;l++) add(f[k+1][al-(now-l-1)][l],tmp);
            for(int l=now+1;l<=al;l++) add(f[k+1][al-(l-now-1)][now+1],tmp);
        }
    }
    int re=0;
    for(int i=1;i<=m;i++) for(int j=1;j<=i;j++) add(re,f[n][i][j]);
    printf("%d\n",re);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值