【CF】Codeforces Round #543 (Div. 1, based on Technocup 2019 Final Round)

比赛出锅了,,好像题解泄露了,五分钟就unrated,失去灵魂orz CF

A Diana and Liana

给你一个m长度的序列,删去一些数之后,保持原有顺序,还剩下若干的数字,然后每k个分一段,分前n段。给你一个multisetS集合,保证某一段中必须要有集合中的所有数字(包括至少要求的个数)。 一切数字5e5 对于每一个L,找到一个最小的R,使得[L,R]中包含S集合中的所有数,然后贪心看行不行。可以考虑到对于每个L,其R保证递增,这样我们加个双指针就可以了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
const int maxn = 500000;
int a[maxn+5];
int m,k,n,s,CT;
int YO[maxn+5],yoh[maxn+5],woc,wc;
int ANS = 1e9,ps;
int main() {
    scanf("%d%d%d%d",&m,&k,&n,&s);
    for(int i=1;i<=m;i++) {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=s;i++) {
        int x; scanf("%d",&x);
        if(YO[x]++==0) woc++;
        yoh[x]++;
    }
    for(int L=1,R=1;L<=m;L++) {
        while(R<=m&&woc>0) {
            if( (--YO[a[R]]) == 0 ) woc--;
            R++;
        }
        if(woc==0) {
            if( (L-1)%k + (R-L-k)<=0 ) {
                puts("0"); return 0;
            } else {
                if( (L-1)%k + (R-L-k) < ANS && m-(L-1)%k-(R-L-k) >= n*k ) {
                    ANS = (L-1)%k + (R-L-k); ps = L;
                }
            }
        }
        if(YO[a[L]]++==0) woc++;
    }
    if(ANS==1e9) return puts("-1"),0;
    printf("%d\n",ANS);
    for(int i=(ps-1)/k*k+1;i<ps;i++) {
        printf("%d ",i); ANS--;
    }
    for(int i=ps;i<=m&&ANS;i++) {
        if( (yoh[a[i]]) ) {
            yoh[a[i]]--;
        } else {
            printf("%d ",i); ANS--;
        }
    }
}

C Compress String

给一个5000的字符串s要求将其拆分为t1+t2+t3+t.....如果ti是t1+t2..t_{i-1}的子串,那么需要花费代价b,否则如果ti长度1,代价a,最小代价。 可以很快想到一个dp,但是n^3? 我们发现如果一个模式串在上一位的dp中不能作为子串,那么添加一个字符之后也不会是子串。。。所以又是双指针。。。加个KMP是n^2的。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n,a,b;
char ss[5005];
int f[5005],fail[5005];
void getfail(int fr,int to) {
    fail[1] = 0; int j = 0;
    for(int i=fr+1;i<=to;i++) {
        while(j&&ss[i]!=ss[fr+j]) j = fail[j];
        if(ss[i]==ss[fr+j]) j++;
        fail[i-fr+1] = j;
    }
}
bool KMP(int fr,int to,int l,int r) {
    if(r-l+1>to-fr+1) return 0;
    int j = 0;
    j = 0;
    for(int i=fr;i<=to;i++) {
        while(j&&ss[i]!=ss[l+j]) j = fail[j];
        if(ss[i]==ss[l+j]) j++;
        if(j==r-l+1) return 1;
    }
    return 0;
}
int main() {
    scanf("%d%d%d",&n,&a,&b);
    scanf("%s",&ss[1]);
    f[1] = a; int p = 1;
    for(int i=2;i<=n;i++) {
        f[i] = f[i-1] + a;
        while(p<i) {
            getfail(p+1,i);
            if(KMP(1,p,p+1,i)){
                for(int j=p;j<i;j++) f[i] = min(f[i],f[j]+b);
                break;
            }
            p++;
        }
    }
    printf("%d",f[n]);
}

C

题意简述:要求你压缩一个字符串,假设你将S压缩成了t1t2...tk,那么对于一个串ti。 若|ti|=1可以花a价格压缩,如果ti是t1+t2+..t_{i−1}的子集可以花b价格压缩,问压缩的最小代价。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n,a,b;
char ss[5005];
int f[5005],fail[5005];
void getfail(int fr,int to) {
    fail[1] = 0; int j = 0;
    for(int i=fr+1;i<=to;i++) {
        while(j&&ss[i]!=ss[fr+j]) j = fail[j];
        if(ss[i]==ss[fr+j]) j++;
        fail[i-fr+1] = j;
    }
}
bool KMP(int fr,int to,int l,int r) {
    if(r-l+1>to-fr+1) return 0;
    int j = 0;
    j = 0;
    for(int i=fr;i<=to;i++) {
        while(j&&ss[i]!=ss[l+j]) j = fail[j];
        if(ss[i]==ss[l+j]) j++;
        if(j==r-l+1) return 1;
    }
    return 0;
}
int main() {
    scanf("%d%d%d",&n,&a,&b);
    scanf("%s",&ss[1]);
    f[1] = a; int p = 1;
    for(int i=2;i<=n;i++) {
        f[i] = f[i-1] + a;
        while(p<i) {
            getfail(p+1,i);
            if(KMP(1,p,p+1,i)){
                for(int j=p;j<i;j++) f[i] = min(f[i],f[j]+b);
                break;
            }
            p++;
        }
    }
    printf("%d",f[n]);
}

D Power Tree

给一棵树,现在A可以从树上面选一些点出来,选ii的花费为ai,然后B给所有叶子结点赋值,然后A可以对所有选出来的点进行一次操作:使得以它为根的子树中的所有叶子结点全部减去一个相同的值,问如果A想保证无论B怎么赋值自己操作之后每个叶子的权值都为0那么选出来点的最少代价是多少并要求列出所有可能被选出的点。 设定f[x][0/1]表示以x的子树中还有一个点没有被覆盖的最小代价。 设定sum[x] 为x的所有儿子的f[y][0]之和。 考虑dp转移之后最后答案f[x][0]。 最后根据你怎么转移的,然后看看能不能对上一个状态逆转移,如果可以就继续搜,搜出所有可能的转移过程,就可以了。 具体look code:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#define pr pair<int,int>
#define fi first
#define se second
#define int long long
using namespace std;
const int maxn = 2e5+5;
vector<int>ve[maxn];
int n;
int c[maxn];
int fa[maxn];
int sum[maxn];
int dp[maxn][2];
bool vis[maxn][2];
void dfs(int x,int ba) {
    fa[x] = ba;
    if(ve[x].size()==1&&(ba)) {
        dp[x][0] = c[x]; 
        dp[x][1] = 0;
        return;
    }
    for(auto y:ve[x]) {
        if(y==ba) continue;
        dfs(y,x);
        sum[x] += dp[y][0];
    }
    dp[x][0] = sum[x];
    for(auto y:ve[x]) {
        if(y==ba) continue;
        dp[x][0] = min(dp[x][0],sum[x]-dp[y][0]+dp[y][1]+c[x]);
        dp[x][1] = min(dp[x][1],sum[x]-dp[y][0]+dp[y][1]);
    }
}
queue<pr>q;
bool KN[maxn];
main() {
    memset(dp,0x3f,sizeof dp);
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) {
        scanf("%lld",&c[i]);
    }
    for(int i=1;i<n;i++) {
        int x,y; scanf("%lld%lld",&x,&y);
        ve[x].push_back(y);
        ve[y].push_back(x);
    }
    dfs(1,0);
    vis[1][0] = 1; q.push(pr(1,0));
    while(q.size()) {
        int x = q.front().fi; int y = q.front().se; q.pop();
        if(fa[x]&&ve[x].size()==1) {
            if(!y)KN[x] = 1; continue;
        }
        if((!y)&&sum[x]==dp[x][0]) {
            for(auto p:ve[x]) {
                if(p==fa[x]) continue;
                if(!vis[p][0]) q.push(pr(p,0)),vis[p][0] = 1;
            }
        }
        int pre = -1;
        for(auto p:ve[x]) {
            if(p==fa[x]) continue;
            if( ( (!y) && dp[x][0] == sum[x] - dp[p][0] + dp[p][1] + c[x] ) || ( (y) && dp[x][1] == sum[x] - dp[p][0] + dp[p][1] ) ) {
                if(!y) KN[x] = 1;   
                if(pre==-1) {
                    pre = p;
                    for(auto g:ve[x]) {
                        if(g==p||g==fa[x]) continue;
                        if(!vis[g][0]) vis[g][0] = 1 , q.push(pr(g,0));
                    }
                } else if(pre!=-2) {
                    if(!vis[pre][0]) vis[pre][0] = 1 , q.push(pr(pre,0));
                    pre = -2;
                }
                if(!vis[p][1]) vis[p][1] = 1,q.push(pr(p,1));
            }
        }
    }
    int ANS = 0;
    for(int i=1;i<=n;i++) {
        ANS += KN[i];
    }
    printf("%lld %lld\n",dp[1][0],ANS);
    for(int i=1;i<=n;i++) {
        if(KN[i]) printf("%lld ",i);
    }
}

E

题意简述:给出一个a,(a≤1000),现在要求找出一个n满足:S(a∗n)=S(n) * a S(x)表示x各位数字之和。 数位dp(还是神奇的dp+bfs) f[x][y][0/1]从低位向高位枚举。 x 表示之前的位数 * A 之后累加到这一位上要进的位(这一位以及接下来要加的值)。 y 表示之间的数 * A 的数的 数位和 * A 减去 当前的数位之和。 据说$x\in[0,1000],y\in[-2000,2000] $这样时间复杂度对的。每次转移枚举当前位,并且记录下从哪里转移过来就好。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
struct nd{
    int a,b,c;
};
const int py = 2010;
nd pre[1005][4050][2];
int dig[1005][4050][2];
bool vis[1005][4050][2];
queue<nd>q;

int A;
int st[500005],tp;
void solve() {
    for(int i=0;i<=9;i++) {
        int nfi = i*A/10;
        int tdj = i*A%10;
        vis[nfi][py+tdj*A-i][i!=0] = 1;
        q.push((nd){nfi,py+tdj*A-i,i!=0});
        pre[nfi][py+tdj*A-i][i!=0] = {-1,-1,-1};
        dig[nfi][py+tdj*A-i][i!=0] = i;
    }
    while(q.size()) {
        int x = q.front().a; int y = q.front().b; int z = q.front().c;
        q.pop();
        if(x==0&&y==py&&z) {
            bool ff = 0;
            while(x+y+z!=-3) {
                if(ff||dig[x][y][z]!=0)printf("%d",dig[x][y][z]),ff=1;
                auto oo = pre[x][y][z];
                x = oo.a; y = oo.b; z = oo.c;
            }
            return;
        }
        for(int i=0;i<=9;i++) {
            int nfi = (i*A+x)/10;
            int tdj = (x+i*A)%10;
            int vv = y + tdj*A - i;
            bool fl = z | (i!=0);
            if(vv>0&&vv<4050&&!vis[nfi][vv][fl]) {
                vis[nfi][vv][fl] = 1;
                q.push((nd){nfi,vv,fl});
                pre[nfi][vv][fl] = (nd){x,y,z};
                dig[nfi][vv][fl] = i;
            } 
        }
    }
    puts("-1");
}

int main() {
    scanf("%d",&A);
    solve();
}

F

题意简述: 两个人W,P需要互相送信。 现在有n个时间点t1,t2,...,tn,每个时间点W/P负责送信,以及一个结束时间点tn+1。 有两种选择: 直接花d的代价送给对方 将信寄存在R那里,设从寄存到拿信花了T个单位时间,代价是c∗T。 一个人可以在R那儿拿信当且仅当自己去R送信或者时间为tn+1 考虑到一旦有一个人放了信到朋友R那里,那么从该时间点T一直到最后每一刻一定会花c的价格。 (如果W不放了P一定放,P不放了W一定放) 那么我们枚举在从哪里开始放了第一封信到朋友R那里就可以了。
#include<iostream>
#include<cmath>
#include<cstring>
#define int long long
using namespace std;

int n,c,d;

int t[100005];
int ty[100005];
char w[100005];
main() {
    cin>>n>>c>>d;
    for(int i=1;i<=n;i++) {
        cin>>t[i]>>w[i];
        if(w[i]=='W') ty[i] = 1;  else ty[i] = 0;
    }
    cin>>t[n+1]; ty[n+1] = 3;
    int lst = t[n+1];
    int oz = 0;
    int ans = d*n;
    for(int i=n;i>=1;i--) {
        if(ty[i]==ty[i+1]) {
            oz += min((lst - t[i+1])*c,d);
        } else lst = t[i+1];
        ans = min(ans,oz + (i-1)*d + (t[n+1]-t[i])*c );
    }
    cout<<ans;
}
posted @ 2019-03-04 14:49  Newuser233  阅读(9)  评论(0编辑  收藏  举报