题目链接:http://codeforces.com/contest/796

A:水题,看懂题意然后模拟一下即可。复杂度O(n)

///CF 796A

#include <bits/stdc++.h>
using namespace std;
int n, m, k;
int a[110];
int main(){
    scanf("%d%d%d", &n,&m,&k);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i]);
    }
    int ans = 1e9;
    for(int i=m-1; i>=1; i--){
        if(a[i]!=0&&a[i]<=k){
            ans = min(ans, (m-i)*10);
        }
    }
    for(int i=m+1; i<=n; i++){
        if(a[i]!=0&&a[i]<=k){
            ans = min(ans, (i-m)*10);
        }
    }
    cout<<ans<<endl;

}

B,同模拟水题,复杂度O(k)

///CF 796B

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int n, m, k;
int vis[maxn];

int main(){
    scanf("%d%d%d", &n,&m,&k);
    for(int i=1; i<=m; i++){
        int x; scanf("%d", &x);
        vis[x]=1;
    }
    int curpos = 1;
    int ans = -1;
    int flag = 0;
    while(k--){
        int x, y;
        scanf("%d%d", &x, &y);
        if(flag) continue;
        if(x==curpos&&vis[x]){
            flag=1;
            ans=x;
        }
        else if(y==curpos&&vis[y]){
            flag=1;
            ans=y;
        }
        else if(x==curpos){
            if(vis[y]){
                ans = y;
                flag=1;
            }
            else{
                curpos=y;
            }
        }
        else if(y==curpos){
            if(vis[x]){
                ans = x;
                flag = 1;
            }
            else{
                curpos = x;
            }
        }
    }
    if(ans==-1) ans = curpos;
    cout<<ans<<endl;
    return 0;
}

C:题意就是给了一颗树,每个节点有一个能量值,现在一个人有个初始能量值,现在他选择一个点去破坏,

但是破坏之后这个点的相邻点和相邻点的相邻点的能量值会加1,破坏一个点的条件是这个人当前的能量值

不小于这个点的能量值。问破坏图中所有节点具有的初始能量的最小值。

解法:思维,思考一下发现如果我们选择最大的好像很能逼近正确答案,那么一定是最大吗?并不是我们可以

简单举出一些例子发现答案可以是最大值加1或者最大值加2,但是好像没有其他答案了?的确,我们深入思

考,这是一颗树,考虑极限情况就是1条链并且所有值都等于最大值时,我们用最大值加2就可以了。那么

什么情况是最大值,什么情况是最大值加一,什么情况是最大值加二呢?

最大值:只有一个点的值等于mx,相邻点包含了全部mx-1的点

最大值加一:存在一个点,它和它相邻的点,包含了全部的权值为最大值的点。

最大值加二 : 其他。

并且只有一个点为mx并且不满足条件1的时候,答案是mx+1。

///CF 469C

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+10;
map <int, int> mp1;
int n, a[maxn];
vector<int>G[maxn];
int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    for(int i=1; i<n; i++)
    {
        int x, y;
        scanf("%d%d", &x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    int mx = -2e9;
    for(int i=1; i<=n; i++) mx = max(a[i], mx);
    for(int i=1; i<=n; i++)
    {
        mp1[a[i]]++;
    }
    int ans = mx+2;
    if(mp1[mx]==1)
    {
        int pos;
        for(int i=1; i<=n; i++) if(a[i]==mx)
        {
            pos=i;
            break;
        }
        int cnt = 0;
        for(int i=0; i<G[pos].size(); i++)
        {
            int v = G[pos][i];
            if(a[v]==mx-1) cnt++;
        }
        if(cnt==mp1[mx-1]) ans = mx;
        else ans=mx+1;
        printf("%d\n", ans);
        return 0;
    }
    else for(int i=1; i<=n; i++){
        int cnt=0;
        if(a[i]==mx) cnt++;
        for(int j=0; j<G[i].size(); j++)
        {
            int v = G[i][j];
            if(a[v]==mx) cnt++;
        }
        if(cnt==mp1[mx])
        {
            ans = mx+1;
            printf("%d\n", ans);
            return 0;
        }
    }
    printf("%d\n", ans);
}

D:

题意:n个城市,某些城市有警察局,共k个。定义了一个规则,就是任意一个点离它最近的警察局的距离都要

不超过d。边权是1,现在问在满足这个规则的条件下最多删除多少条边?

解法:开始想的是,删除的边肯定和k个点能延伸d步能覆盖到的边的覆盖,然后就不知道怎么做了。然后观摩

了一发我队友的代码,偷取了一个思路。。。把k个起点放到队列里面,进行多起点BFS。维护两个标记,一

个标记边,一个标记点,如果当前边没有被标记但是当前点的下一个点被标记了,则说明了当前边是可以删除

的,理由很简单,因为BFS是按照距离从小到大跑的,如果当前边没有被标记但是当前点的下一个点被标记

了,也就说明了dis[u]<=d&&dis[v]<=d。并且我们这道题根本不需要关心d,因为题目说了初始状态是合法的

那么BFS跑最短路怎么都不会跑到大于d的距离。

///CF 796D

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+10;
///u&&v连通 dis[u]<=d&&dis[v]<=d
///本质上变成了本题实际和d无关,因为初始就是合法状态

struct node{
    int v, id;
};
int n, m, d;
vector <node> G[maxn];
int vis1[maxn], vis2[maxn];
queue <int> que;
int bfs()
{
    int ans = 0;
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        for(int i=0; i<G[u].size(); i++){
            node nxt = G[u][i];
            if(vis1[nxt.id]) continue;
            if(vis2[nxt.v]){
                vis1[nxt.id]=2;
                ans++;
                continue;
            }
            vis2[nxt.v]=1;
            vis1[nxt.id]=1;
            que.push(nxt.v);
        }
    }
    printf("%d\n", ans);
}
int main()
{
    scanf("%d%d%d", &n,&m,&d);
    for(int i=0; i<m; i++){
        int x;
        scanf("%d", &x);
        vis2[x]=1;
        que.push(x);
    }
    for(int i=1; i<n; i++){
        int u, v;
        scanf("%d%d", &u,&v);
        G[u].push_back(node{v, i});
        G[v].push_back(node{u, i});
    }
    int ans = bfs();
    //cout<<ans<<endl;
    for(int i=1; i<n; i++){
        if(vis1[i]==2) printf("%d ", i);
    }
    cout<<endl;
}

E

题意:有一个人要考试作弊, 她可以偷看左右两个人的试卷。但是她只能偷看p次,每次只能看一个人试卷上

的连续的k个题目。总共有n道题目,给你左右两个人能做对的题目,求出她最大能做对几道题。

一个样例:她共有2次偷看的机会,每次只能看3道题目,第一次偷看person1的(1,2,3), 第二次偷看

person2的(4, 5, 6),可以作对的题目为(1, 3, 5, 6)共4道

解法:DP

dp[i][j][x][y]表示考虑完前i个问题,当前用掉了j次机会,第一个人还能看x道题,第二个人还能看j道题的最优值。

这里的复杂度O(p*k*k),但是当p>(2*n)/k时可以全选,那么直接全选就行了。那么剩下的我们就跑个DP就

好了。复杂度是O(n*n*k)

那么dp的转移这里用被动转移好写一点,并且注意到数组直接是开不下的,观察转移发现可以滚动一下,这

题就完了。转移看代码,不难。

///CF 769E

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1002;
int n, p, k, a[maxn], b[maxn], ans;
int n1, n2, dp[2][maxn][52][52];
///dp[i][j][x][y]表示考虑完前i个问题,当前用掉了j次机会,第一个人还能看x道题,第二个人还能看j道题的最优值
void update(int &x, int y) {if(y>x) x=y;}
int now=0,pre=1;

int main()
{
    scanf("%d%d%d", &n,&p,&k);
    scanf("%d",&n1);
    for(int i=1; i<=n1; i++){
        int x;
        scanf("%d", &x);
        a[x]++;
    }
    scanf("%d", &n2);
    for(int i=1; i<=n2; i++){
        int x;
        scanf("%d", &x);
        b[x]++;
    }
    int lim=n/p;
    if(n%p!=0) lim++;
    lim*=2;
    if(k>=lim){
        for(int i=1; i<=n; i++){
            ans += a[i]|b[i];
        }
        printf("%d\n", ans);
        return 0;
    }
    memset(dp, 0xef, sizeof(dp));
    dp[0][0][0][0]=0;
    for(int i=1; i<=n; i++){
        swap(now,pre);
        memset(dp[now], 0xef, sizeof(dp[now]));
        for(int j=0; j<=p; j++){
            for(int ii=0; ii<=k; ii++){
                for(int jj=0; jj<=k; jj++){
                    if(!ii&&!jj){
                        update(dp[now][j][0][0], dp[pre][j][0][0]);
                        update(dp[now][j+1][k-1][0], dp[pre][j][0][0]+a[i]);
                        update(dp[now][j+1][0][k-1], dp[pre][j][0][0]+b[i]);
                        update(dp[now][j+2][k-1][k-1], dp[pre][j][0][0]+(a[i]|b[i]));
                    }
                    else if(!ii){
                        update(dp[now][j][0][jj-1], dp[pre][j][0][jj]+b[i]);
                        update(dp[now][j+1][k-1][jj-1], dp[pre][j][0][jj]+a[i]);
                        update(dp[now][j+2][k-1][k-1], dp[pre][j][0][jj]+(a[i]|b[i]));
                    }
                    else if(!jj){
                        update(dp[now][j][ii-1][0], dp[pre][j][ii][0]+a[i]);
                        update(dp[now][j+1][ii-1][k-1], dp[pre][j][ii][0]+b[i]);
                        update(dp[now][j+2][k-1][k-1], dp[pre][j][ii][0]+(a[i]|b[i]));
                    }
                    else update(dp[now][j][ii-1][jj-1], dp[pre][j][ii][jj]+(a[i]|b[i]));
                }
            }
        }
    }
    for(int l=0; l<=p; l++){
        for(int i=0; i<=k; i++){
            for(int j=0; j<=k; j++){
                ans = max(ans, dp[now][l][i][j]);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}