2018 USP-ICMC

简单题 B D F L

中等难度题 E I

更难一点得题 A C G

难题 H K J

 

B. Ugly Number 

这个题目很简单,不过我的方法有点点小问题,不过可以改进一下就应该没什么问题了。

这个题目就是让你判断不断把最后一位移到最前面来组成的新数字会不会有比原来小的,

这个题目就先判断后面有没有数字比第一个小,有的话就不对了

除了这个还有一种可能就是后面的数字和原来第一位的数字一样,那么就需要判断和第一位一样的数字后面跟的数和第一位跟的数哪个更大。

 

#include 
#include 
#include 
#include 
#include 
#include 
#include <string>
#include 
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 5e5 + 100;
char s[maxn];
int main()
{
    int n;
    cin >> n;
    cin >> s;
    int num = 0;
    int ok = 0;
    for(int i=1;i)
    {
        if (s[i] < s[0]) ok = 1;
        if(s[i]==s[0])
        {
            num = 0;
            int x = i, y = 0;
            while(s[x]==s[y])
            {
                x = (x + 1) % n;
                y = (y + 1) % n;
                num++;
                if (num > 100) break;
            }
            if (s[x] < s[y]) ok = 1;
        }
    }
    if (ok==0) printf("Yes\n");
    else printf("No\n");
    return 0;
}

 

 

F. Number Preference

这个就是给你一些人对于数字的喜好,让你求出最后他们都喜欢的数字有多少

先给你n代表有n个人

每一个人一列,如果第一个数字是1 代表这是说这个人喜好的数字,如果是2代表这是说这个人讨厌的数字

这个题目比较简单,因为数字很大就需要用map来存,不过map也不需要很大,

两个map  如果有1出现的人,那就只需要一个map来存,没有就需要第二个来存2的数字。

如果有1出现,那么就把第一个人当作基准,如果后面的人喜好(1)就++,不喜欢就删去(2)

如果只有2 那就用另一个map来判断就可以了。

 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include <string>
#include 
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 100;
const ll mx = 1e18;
mapint>mp;
mapint>two;
int main()
{
    int n;
    scanf("%d", &n);
    ll num = 0;
    int nu1 = 0, nu2 = 0;
    int first = 0;
    for(int i=1;i<=n;i++)
    {
        int opt, len;
        scanf("%d%d", &opt, &len);
        if(opt==1&&first==0)
        {
            nu1++;
            first = 1;
            for(int j=1;j<=len;j++)
            {
                ll x;
                scanf("%lld", &x);
                mp[x]++;
                //printf("1  mp[%lld]=%d\n", x, mp[x]);
            }
        }
        else if(opt==1)
        {
            nu1++;
            for(int j=1;j<=len;j++)
            {
                ll x;
                scanf("%lld", &x);
                if (mp[x])
                {
                    mp[x]++;
                //    printf("2 mp[%lld]=%d\n", x, mp[x]);
                }
            }
        }
        else if(opt==2)
        {
            nu2++;
            for(int j=1;j<=len;j++)
            {
                ll x;
                scanf("%lld", &x);
                if (mp[x]) mp[x] = 0;
                two[x]++;
            }
        }
    }
    if(nu1)
    {
        mapint>::iterator p;
        for(p=mp.begin();p!=mp.end();p++)
        {
            //printf("%lld %d\n", p->first, p->second);
            if (p->second >= nu1) num++;
        }
        printf("%lld\n", num);
        return 0;
    }
    ll ans = 0;
    mapint>::iterator p;
    for (p = two.begin(); p != two.end(); p++) ans++;
    printf("%lld\n", mx - ans);
    return 0;
}

 

 

D. Checkerboard

这个题目题意读懂就好了,这个就是从起点到终点只可以用最多n步,每一个位置都可以重复走,然后问你从起点到终点可以有多少种不同的步数。

 

#include
#include
#include
#include
#include 
#include 
using namespace std;
typedef long long ll;
const int Max = 1e5 + 10;
const int inf = 0x3f3f3f3f;

int main()
{
    ll n, x1, x2, y1, y2;
    scanf("%lld%lld%lld%lld%lld", &n, &x1, &y1, &x2, &y2);
    ll  m = abs(x1 - x2) + abs(y1 - y2);
    if (m > n)
    {
        printf("0\n");
    }
    else if (m == 0)
    {
        printf("%lld\n", (n - m) / 2);
    }
    else
    {
        printf("%lld\n", (n - m) / 2 + 1);
    }
    return 0;
}

 

 

L. PC is for kicking

这个就是一棵树,给了你起点,问你从这一点开始不重复的走能走到的最多的点是多少。

 

#include 
using namespace std;
typedef long long ll;
const int Max = 1e5 + 10;
const int inf = 0x3f3f3f3f;

vector<int> v[Max];
bool vis[Max];    //是否以走
int depth[Max];

int build(int now)
{
    queue<int>q;
    q.push(now);
    vis[now] = true;
    depth[now] = 1;
    int cnt = 0;
    while (!q.empty())
    {
        int s = q.front();
        q.pop();
        vis[s] = true;
        for (int i = 0; i < v[s].size(); i++)
        {
            int e = v[s][i];
            if (!vis[e])
            {
                depth[e] = depth[s] + 1;
                q.push(e);
            }
        }
        cnt = max(cnt, depth[s]);
    }
    return cnt;
}

int main()
{
    int n, now;
    while (scanf("%d%d", &n, &now)!=EOF)
    {
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= n; i++)
        {
            v[i].clear();
        }
        for (int i = 0; i < n - 1; i++)
        {
            int s, e;
            scanf("%d%d", &s, &e);
            v[s].push_back(e);
            v[e].push_back(s);
        }
        printf("%d\n", build(now));
    }
    return 0;
}

 

 

E. Loppinha, the boy who likes sopinha
 
这个是一个动态规划,用记忆化搜索比较好一点。我是不会写,但是看了题解之后,感觉这个状态挺难定义的。
 
#include 
#include 
#include 
#include 
#include 
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 100;
int a[maxn];
int dp[550][550];//表示已经处理了前面的i个,然后还可以处理j次需要消耗的能量。
int n, m;

int cul(int x)
{
    return x * (x + 1) / 2;
}

int dfs(int p,int k)
{
    if (k < 0) return inf;//如果k<0这个是不应该出现的情况,如果出现了,说明交换的次数超了,所以用inf表示不可能
    if (p >= n) return 0;//如果到达p==n的同时k>=0,这个就说明这个情况是合理的,而且p==n的时候就不会需要消耗能力了。
    if (dp[p][k] != -1) return dp[p][k];
    if (a[p] == 0) return dfs(p + 1, k);//如果这一个值它是0就不需要做过多的考虑
    int i = 0;
    dp[p][k] = inf;
    for (i = p; i < n&&a[i] == 1; i++)
    {
        dp[p][k] = min(dp[p][k], dfs(i + 1, k - 1) + cul(i - p));//转移方程
    }
    dp[p][k] = min(dp[p][k], dfs(i + 1, k) + cul(i - p));//这个是考虑在出现0之前的每一个1都不删去。
    return dp[p][k];
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) scanf("%1d", &a[i]);
    memset(dp, -1, sizeof(dp));
    for(int i=0;i<=n;i++)
    {
        if(dfs(0,i)<=m)
        {
            printf("%d\n", i);
            return 0;
        }
    }
}

 

 I. I Will Go

题目大意就是给你一个人他的好朋友,定下规则,就是这个人去宴会的唯一原因就是他好朋友去了,但是他好朋友去了他不一定去,

意思就是如果一个人去了,那么他的好朋友一定去了,但是如果他的好朋友去了他不一定去,这个不会成环,

举个例子 如果a是b的好朋友那么b就不是a的好朋友。

这个题目就是利用这个关系建树,如果在同一棵树上一个人的深度比另一个人的浅,那就说明这个人会去,反之则不会去,这个树自己画一下就明白了。

但是这里有很多棵树,这个就很麻烦。

 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 100;
int l[maxn], r[maxn];
int head[maxn];
bool vis[maxn];
struct node
{
    int u, v, nxt;
}exa[maxn];
int cnt = 0;
void add(int u,int v)
{
    exa[cnt].u = u;
    exa[cnt].v = v;
    exa[cnt].nxt = head[u];
    head[u] = cnt++;
}
int tot = 0;
void dfs(int u)
{
    l[u] = ++tot;
    for(int i=head[u];i!=-1;i=exa[i].nxt)
    {
        dfs(exa[i].v);
    }
    r[u] = ++tot;
}

int main()
{
    int n, m;
    memset(vis, 0, sizeof(vis));
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m);
    for(int i=0;i)
    {
        int x;
        scanf("%d", &x);
        if (x != -1)
        {
            add(x, i);
            vis[i] = 1;
        }
    }
    for(int i=0;i)
    {
        if (!vis[i]) dfs(i);
    }
    while(m--)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        if (l[u] >= l[v] && r[u] <= r[v]) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

 

A. Nicoleta and the circle of kids

 

这个题目是求最大生成树的所有边之和,和最小生成树差不多,但是因为这个数据太大无法建图,所以不能用那种方法。

先说说这个是怎么建图的,这个就是任意一个人他可以连所有离他的距离小于等于k的人,他们两个直接的边的权值为他们的距离。

但是既然是求最大生成树,那么就肯定边越长越好,所以我们都取k。

例如,我们任选一个人a开始,那么他会连到a+k 然后a+k这个人又会往后面连 a+k就可以连到 a+2k这个人 以此类推 就是一个点集 a+m*k

这个题目有难就难在这里有一个取模操作意思就是 n=3 k=2 那么第0个人是不是会连到第二个人 然后第二个人+2对n取模是不是会连到第1个人。

所以这个点集为 (a+m*k)%n 这个取模操作就让题目变得复杂了,我们可以消除这个取模操作

就是 a+m1*z=a+m*k%n  这个z=gcd(n,k)  所以就是以一个点为起点就有 n/z这么多个人可以连在一起,他们直接的权值都是k

然后一共最多有z个这样的圈子,这些圈子都连了边了,只有圈子之间没有连边了,可以证明的是,这些圈子之间无法连长度为k的边,但是可以连长度为k-1的边。

 

#include 
#include 
#include 
using namespace std;
typedef long long ll;

ll gcd(ll a,ll b)
{
    return b == 0 ? a : gcd(b, a%b);
}

int main()
{
    ll n, k;
    cin >> n >> k;
    ll z = gcd(n, k);
    ll ans = k * (n / z - 1)*z + (k - 1)*(z - 1);
    printf("%lld\n", ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/EchoZQN/p/10860415.html

你可能感兴趣的:(2018 USP-ICMC)