Educational Codeforces Round 46 (Rated for Div. 2)

A. Codehorses T-shirts

题意:找出两份名单中不同衣服大小的个总件数
解法:map模拟一下就行

#include
using namespace std;
typedef long long LL;
map<string,int>mp,mp2;
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        static string x;
        cin>>x;
        mp[x]++;
    }
    for(int i=1;i<=n;i++){
        static string x;
        cin>>x;
        mp2[x]++;
    }
    int ans=0;
    for(auto it:mp2){
        if(mp[it.first]<=it.second) ans+=it.second-mp[it.first];
    }
    cout<return 0;
}

B. Light It Up

题意:由程序控制n栈灯,分别在a[i]时刻转换状态,初始是亮着的。问向程序中添加一个时间点,灯的总亮着时间之和最长。
解法:枚举在每个时间点前加入一个点,然后计算灯亮的总时长。可以从后往前求出每个时间点的灯亮时间的后缀和,可以得出在第i个点加入一个点后灯亮最时长就为sum[0]-sum[i]+state+M-a[i]-sum[i] , 其中sum[0]-sum[i]+state 表示在i点加一个点后前i时间灯亮的时间,若在i点灯亮,state=-1 ,否则state=-1M-a[i]-sum[i] 表示后i时间在状态转变后的灯亮时间。

#include
using namespace std;
typedef long long LL;
int a[100005],sum[100005];
int n,M;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>M;
    for(int i=1;i<=n;i++) cin>>a[i];
    a[n+1]=M;
    int cur=(n+1)%2;
    for(int i=n;i>=0;i--){
        if(cur) sum[i]=sum[i+1]+a[i+1]-a[i];
        else sum[i]=sum[i+1];
        cur=1-cur;
    }
   // for(int i=0;i<=n;i++) cout<
    cur=1;
    int maxx=sum[0];
    for(int i=1;i<=n;i++){
        int temp=sum[0]-sum[i]+M-a[i]-sum[i];
        if(cur) temp--;
        else temp++;
        maxx=max(maxx,temp);
    }
    cout<return 0;
}

C. Covered Points Count

题意:给n个线段的左右端点,每条线段可以覆盖从左端点到右端点的闭区间的所有点,问分别被覆盖了1~n次的点有多少个。
解法:对点坐标进行离散化,离散化的时候对于值相差大于1的两个数中间要插入一个数。

#include
using namespace std;
typedef long long LL;
const int maxn=2e5+5;
LL cnt[maxn],a[maxn*8];
int n;
pairp[maxn];
vectorv;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>p[i].first>>p[i].second;
        v.push_back(p[i].first);
        v.push_back(p[i].second);
    }
    sort(v.begin(),v.end());

    v.erase(unique(v.begin(),v.end()), v.end());

    int siz=v.size();
    for(int i=0;i1;i++) if(v[i]+1!=v[i+1]) v.push_back(v[i]+1);
    sort(v.begin(),v.end());
    for(int i=1;i<=n;i++){
        int l=lower_bound(v.begin(), v.end(), p[i].first)-v.begin();
        int r=lower_bound(v.begin(), v.end(), p[i].second+1)-v.begin();
        a[l]++;a[r]--;
    }

    siz=int(v.size());
    for(int i=0;i<=siz;i++) a[i]+=a[i-1];
    //for(int i=0;i<=v.size();i++) cout<
    //for(int i=0;i
    for(int i=0;i1;i++){
        cnt[a[i]]+=v[i+1]-v[i];//这里不包含v[i+1]这个点
    }
    cnt[a[siz-1]]++;
    for(int i=1;i<=n;i++) cout<' ';
    return 0;
}

D. Yet Another Problem On a Subsequence

题意:如果一个array的第一个数字等于该数组长度减1,则被成为good array;一个sequence能被分成若干个good array则称为good sequence。计算原始序列的子序列中good sequence的个数
解法:看了题解才写出来的。。。
dp[i]表示以第i为开头的good sequence的个数,dp[n+1]初始化为1。从n遍历到1,如果a[i]<=0,则dp[i]=0,否则从i+a[i]+1遍历到n+1,dp[i] += dp[j]*c(a[i], j-i+1)。

dp好弱,真是个菜鸡

#include
using namespace std;
typedef long long LL;
const LL mod=998244353;
const int maxn=1005;
int a[maxn];
LL dp[maxn],c[maxn][maxn];
int main()
{
    for(int i=0;i0][i]=1;
    for(int i=1;i0]=0;
    for(int i=1;ifor(int j=1;j1]+c[i-1][j-1];
            c[i][j]%=mod;
        }
    }
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    dp[n+1]=1;
    for(int i=n;i>=1;i--){
        if(a[i]<=0) continue;
        for(int j=i+a[i]+1;j<=n+1;j++)
            dp[i]=(dp[i]+dp[j]*c[a[i]][j-i-1])%mod;
    }
    LL ans=0;
    for(int i=1;i<=n;i++) ans=(ans+dp[i])%mod;
    cout<return 0;
}

E. We Need More Bosses

题意:在无向图中,如果把起点到终点路径上一条边删去,则不能从起点走到终点,就可以在这条边上放置一个boss,问图中任意两点之间最多放置多少个boss
解法:tarjan缩点后求树的直径

#include
using namespace std;
typedef long long LL;
struct node{
    int to,len;
    bool operator<(const node&x)const{
        return lenconst int maxn=3e5+5;
int dfs_n[maxn],low[maxn];
int dfs_clock,sccno[maxn],scc_cnt;
bool vis[maxn];
stack<int>S;
vector<int>e[maxn],e2[maxn];
queueq;
int m,n;
void dfs(int u,int fa)
{
    dfs_n[u]=low[u]=++dfs_clock;
    vis[u]=true;
    S.push(u);
    for(auto v:e[u]){
        if(v==fa) continue;//与有向图的区别在这
        if(!dfs_n[v]){
            dfs(v,u);
            low[u]=min(low[u],low[v]);
        }else if(vis[v]){
            low[u]=min(low[u],dfs_n[v]);
        }
    }
    if(dfs_n[u]==low[u]){
        scc_cnt++;
        int v=S.top();
        do{
            v=S.top();
            S.pop();
            vis[v]=false;
            sccno[v]=scc_cnt;
        }while(u!=v);
    }
}
void tarjan()
{
    for(int i=1;i<=n;i++)
        if(!dfs_n[i]) dfs(i,-1);
}
node bfs(int s)
{
    while(!q.empty()) q.pop();
    memset(vis,0,sizeof vis);
    vis[s]=true;
    q.push({s,0});
    node maxx={0,-1};
    while(!q.empty()){
        node now=q.front();
        maxx=max(maxx,now);
        q.pop();
        int u=now.to;
        for(int v:e2[u]){
            if(!vis[v]){
                vis[v]=true;
                q.push({v,now.len+1});
            }
        }
    }
    return maxx;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    tarjan();
    for(int i=1;i<=n;i++){
        for(int v:e[i]){
            e2[sccno[i]].push_back(sccno[v]);
        }
    }
    cout<1).to).len<return 0;
}

F. One Occurrence

题意:输出询问区间内只出现过一次的数中任意一个
解法:可以用莫对算法来跑,但是复杂度很高,维护只出现一次的数可以采用分块的方法。不知道为什么用链表维护会TLE
正解是线段树或者主席数。
我们用维护一个last数组,表示在当前位置,数x出现的上一个位置。线段树用来维护数x前一次出现的最小位置。如果数x已经在线段树中,就把他删除(将last置为inf),否则将last置为last[i](==-1)。

//莫对算法
//2979ms
#include
using namespace std;
const int maxn=5e5+5;
int block_len;
struct node{
    int l,r,block,id;
    node(){}
    node(int l,int r,int id):l(l),r(r),id(id){
        block=l/block_len;
    }
    bool operator<(const node&x)const{
        if(block!=x.block)
            return lif(block&1)
            return rreturn r>x.r;
    }
}query[maxn];
int n,q,tot;
int cnt[maxn],a[maxn],pos[maxn],b[maxn],ans[maxn];
void add(int x)
{
    cnt[x]++;
    if(cnt[x]==1){
        tot++;
        b[x/block_len]++;
    }else if(cnt[x]==2){
        tot--;
        b[x/block_len]--;
    }
}
void del(int x)
{
    cnt[x]--;
    if(cnt[x]==1){
        tot++;
        b[x/block_len]++;
    }else if(cnt[x]==0){
        tot--;
        b[x/block_len]--;
    }
}
int getans()
{
    if(tot==0) return 0;
    for(int i=0;;i++){
        if(b[i]>0){
            for(int j=i*block_len;;j++){
                if(cnt[j]==1) return j;
            }
        }
    }
}
int main()
{
    scanf("%d",&n);
    block_len=800;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        query[i]=node(l,r,i);
    }
    sort(query+1,query+q+1);
    int l=1,r=0;
    for(int i=1;i<=q;i++){
        node &qr=query[i];
        while(qr.lwhile(qr.r>r){
            r++;
            add(a[r]);
        }
        while(qr.l>l){
            del(a[l]);
            l++;
        }
        while(qr.rfor(int i=1;i<=q;i++) printf("%d\n",ans[i]);
    /////
    return 0;
}
//线段树
//1840ms
//还是很慢
#include
using namespace std;
const int maxn=5e5+5;
const int inf=0x3f3f3f3f;
struct node{
    int last,pos;
    bool operator<(const node&x) const{
        if(last==x.last)
            return pos<x.pos;
        return last<x.last;
    }
}tree[maxn<<2];
struct node2{
    int l,r,id;
    bool operator<(const node2&x)const{
        return r<x.r;
    }
}query[maxn];
int a[maxn],last[maxn],ans[maxn];
int n,q;
void build(int rt,int L,int R)
{
    if(L==R){
        tree[rt]={inf,L};
        return;
    }
    int mid=L+R>>1;
    build(rt<<1,L,mid);
    build(rt<<1|1,mid+1,R);
    tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
void update(int rt,int L,int R,int pos,int last)
{
    if(L==R){
        tree[rt].last=last;
        return;
    }
    int mid=L+R>>1;
    if(pos<=mid)
        update(rt<<1,L,mid,pos,last);
    else
        update(rt<<1|1,mid+1,R,pos,last);
    tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
node getans(int rt,int L,int R,int l,int r)
{
    if(l==L&&r==R)
        return tree[rt];
    int mid=L+R>>1;
    if(r<=mid)
        return getans(rt<<1,L,mid,l,r);
    else if(l>mid)
        return getans(rt<<1|1,mid+1,R,l,r);
    else
        return min(getans(rt<<1,L,mid,l,mid),getans(rt<<1|1,mid+1,R,mid+1,r));
}
int main()
{
    ios::sync_with_stdio(false);
    memset(last,-1,sizeof last);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    cin>>q;
    for(int i=1;i<=q;i++){
        int l,r;
        cin>>l>>r;
        query[i]={l,r,i};
    }
    sort(query+1,query+q+1);
    build(1,1,n);
    int cur=0;
    for(int i=1;i<=q;i++){
        node2 &qr=query[i];
        for(int j=cur+1;j<=qr.r;j++){
            if(last[a[j]]!=-1)
                update(1,1,n,last[a[j]],inf);//删除这个点
            update(1,1,n,j,last[a[j]]);
            last[a[j]]=j;
        }
        node temp=getans(1,1,n,qr.l,qr.r);
        //cout<last<<' '<pos<if(temp.last==inf||temp.last>=qr.l)
            ans[qr.id]=0;
        else
            ans[qr.id]=a[temp.pos];
        cur=qr.r;
    }
    for(int i=1;i<=q;i++)
        cout<return 0;
}

G. Two-Paths

还不会,补题中。。。。。

你可能感兴趣的:(解题报告)