2016-2017 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2016)

A.Art Work Gym - 101550A

题意:给出一个n*m的方格,刚开始全是白色的方格,每次查询把一块区域染成黑色,问白色连通块的数目有多少

分析:用离线的方法来做,从后往前推的答案,联通块用并查集判断

代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define lowbit(x) (x&(-x))
#define mem(a,b) memset(a,b,sizeof(a))
#define FRER() freopen("in.txt","r",stdin);
#define FREW() freopen("out.txt","w",stdout);
using namespace std;
const int maxn = 1000 + 7;
int n,m,q,num;
int a[maxn][maxn],f[maxn*maxn],X1[maxn*10],X2[maxn*10],Y1[maxn*10],Y2[maxn*10],res[maxn*10];
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
bool ok(int x,int y){
    if(1<=x&&x<=n&&1<=y&&y<=m) return true;
    return false;
}
int getid(int x,int y){
    return (x-1)*m+y;
}
int Find(int u){
    return u==f[u]?u:f[u] = Find(f[u]);
}
void unite(int x,int y){
    //cout<<x<<" "<<y<<endl;
    int xx = Find(x) , yy = Find(y);
    if(xx!=yy){
        f[xx] = yy;
        num--;
    }
}
void init(){
    for(int i=0;i<=n*m;i++)
        f[i] = i;
    memset(a, 0, sizeof(a));
    
}
void dfs(int x,int y){
    for(int i=0;i<4;i++){
        int xx = x+dx[i];
        int yy = y+dy[i];
        if(ok(xx, yy)&&!a[xx][yy]){
            unite(getid(x, y), getid(xx, yy));
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    init();
    for(int i=1;i<=q;i++){
        scanf("%d%d%d%d",&X1[i],&Y1[i],&X2[i],&Y2[i]);
        if(X1[i]==X2[i]){
            for(int j=Y1[i];j<=Y2[i];j++)
                a[X1[i]][j]++;
        }else{
            for(int j=X1[i];j<=X2[i];j++)
                 a[j][Y1[i]]++;
        }
    }
    num = n*m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(!a[i][j])
                dfs(i,j);
            else num--;
        }
    }
    for(int i=q;i>=1;i--){
        res[i] = num;
        if(X1[i]==X2[i]){
            for(int j=Y1[i];j<=Y2[i];j++){
                a[X1[i]][j]--;
                if(a[X1[i]][j]) continue;
                num++;
                dfs(X1[i], j);
            }
        }else{
            for(int j=X1[i];j<=X2[i];j++){
                a[j][Y1[i]]--;
                if(a[j][Y1[i]]) continue;
                num++;
                dfs(j, Y1[i]);
            }
        }
    }
    for(int i=1;i<=q;i++)
        printf("%d\n",res[i]);
}

B - Bless You Autocorrect!

题意:

顺序给出n个由小写英文字母组成的单词。

当你需要输打出一个单词时,你每一次可以进行三种操作(输入完第一个字母以后):

                 i.直接输入下一个英文字母。

                 ii.按“tap”键直接打印出单词列表中第一次以已打入字母序列为前缀的单词:e.g.如果第一次以abb为前缀的单词为abbcd,那么打完字母abb以后按”tab“键可以直接打出abbcd。

                 iii.删除已经打出的最后一个字符。

分析:

首先我们需要储存字符串,当然想到了字典树来储存。

相比一个一个地打出字母,这里主要地区别是可以通过按“tap”键来加速打印,故需要在传统地字典树上做出一定地修改:当存入某个单词时,若某一个字母是第一次出现,那么输入完这个字母以后通过“tap”键便可以直接跳转到单词地结尾,这就是在字典树上地一个加边过程,比如以下例子:

 

在建好字典树以后,通过BFS()我们可以求出从根到达每一个节点需要地步数也就是最少地操作次数。上图中地例子可以得到下图所示结果(注意从末节点到中间节点的边为单向边(从中间到末尾打“tap”即可,从末尾到中间只能一个一个删除),相邻节点之间的边为双向边)

 

查询地时候沿着单词往下找即可,中途更新最小值。

时间复杂度O(单词地长度之和)。

代码;

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define lowbit(x) (x&(-x))
#define mem(a,b) memset(a,b,sizeof(a))
#define FRER() freopen("in.txt","r",stdin);
#define FREW() freopen("out.txt","w",stdout);
using namespace std;
const int maxn = 1e6 + 7 , maxch = 26 + 7;
char s[maxn];
struct Trie{
    int ch[maxn][maxch],d[maxn];
    vector<int>g[maxn];
    int tot;
    int idx(char c){return c-'a';}
    void init(){
        tot = 1;
        for(int i=0;i<=1e6;i++) g[i].clear();
        memset(ch, 0, sizeof(ch));
        memset(d, -1, sizeof(d));
    }
    void insert(char *s){
        int len = (int)strlen(s);
        int u = 0;
        vector<int>V;
        for(int i=0;i<len;i++){
            int id = idx(s[i]);
            if(!ch[u][id]){
                g[u].push_back(tot);
                g[tot].push_back(u);
                if(i<len-2) V.push_back(tot);
                ch[u][id] = tot++;
            }
            u = ch[u][id];
        }
        for(int i=0;i<V.size();i++)
            g[V[i]].push_back(u);
        V.clear();
    }
    void bfs(){
        queue<int>q;
        d[0] = 0;
        q.push(0);
        while(!q.empty()){
            int now = q.front();q.pop();
            for(int i=0;i<g[now].size();i++){
                if(d[g[now][i]]==-1){
                    int v = g[now][i];
                    d[v] = d[now]+1;
                    q.push(v);
                }
            }
        }
    }
    int query(char *s){
        int u = 0;
        int len = (int)strlen(s);
        int ans = 0x3f3f3f3f;
        for(int i=0;i<len;i++){
            int id = idx(s[i]);
            if(!ch[u][id]) break;
            u = ch[u][id];
            ans = min(ans,d[u]+len-i-1);
        }
        return min(ans, len);
    }
}t;
int main(){
    int n,m;
    t.init();
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%s",s);
        t.insert(s);
    }
    t.bfs();
    for(int i=0;i<m;i++){
        scanf("%s",s);
        printf("%d\n",t.query(s));
    }
    return 0;
}

C - Card Hand Sorting

题意:给出一封扑克牌,每次可移动一张牌,使得每种花色的牌都按照升序或者降序排列,问最少的移动次数

分析:可暴力枚举排列情况,花色的顺序一共  4!,升序或降序是 2^ 4 总需要枚举的个数为4!*2^4 = 384种

枚举的时候使用二进制枚举,对于移动次数的计算,使用LIS。

代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define lowbit(x) (x&(-x))
#define mem(a,b) memset(a,b,sizeof(a))
#define FRER() freopen("in.txt","r",stdin);
#define FREW() freopen("out.txt","w",stdout);
using namespace std;
const int maxn = 100 + 7 , inf = 0x3f3f3f3f;
int n;
int c[4] = {0,1,2,3};
struct Card{
    int num,color;
    int v;
    int id;
}card[maxn];
map<char,int>m;
bool comp(Card& c1,Card& c2){
    if(c1.color == c2.color) return c1.v < c2.v;
    return c[c1.color]<c[c2.color];
}
void init(){
    m['s'] = 0;
    m['h'] = 1;
    m['d'] = 2;
    m['c'] = 3;
    for(int i=2;i<=9;i++)
        m[i+'0'] = i;
    m['T'] = 10;
    m['J'] = 11;
    m['Q'] = 12;
    m['K'] = 13;
    m['A'] = 14;
}
int LIS(){
    vector<int>v;
    vector<int>::iterator it;
    for(int i=0;i<n;i++){
        it = lower_bound(v.begin(), v.end(), card[i].id);
        if(it==v.end()) v.push_back(card[i].id);
        else *it = card[i].id;
    }
    return (int)v.size();
}
int main(){
    scanf("%d",&n);
    char s[maxn];
    init();
    for(int i=0;i<n;i++){
        scanf("%s",s);
        card[i].id = i;
        card[i].num = m[s[0]];
        card[i].color = m[s[1]];
    }
    int ans = inf;
    do{
        for(int i=0;i<16;i++){
            for(int j=0;j<n;j++){
                if((i>>c[card[j].color])&1) card[j].v =  -card[j].num;
                else card[j].v = card[j].num;
            }
            sort(card, card+n,comp);
            ans = min(ans,n-LIS());
        }
    }while(next_permutation(c, c+4));
    printf("%d\n",ans);
}

D - Daydreaming Stockbroker

题意:买卖股票

分析:峰谷买,峰顶卖

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 400;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll a[maxn];
int main()
{
   // freopen("i.txt","r",stdin);
    int d;
    scanf("%d",&d);
    a[0] = inf;
    for(int i=1; i<=d; i++){
        scanf("%lld",&a[i]);
        if(a[i]==a[i-1]){
            i--;
            d--;
        }
    }
    a[d+1] = -inf;
    ll money = 100;
    ll cnt = 0;
    for(int i=1;i<=d;i++){
        //cout<<i<<" "<<money<<" "<<cnt<<endl;
        if(a[i]<=money){
            if(a[i]<a[i-1]&&a[i]<a[i+1]){
                ll cnttt = money/a[i];
                if(cnt+cnttt>100000)
                    cnttt -= (cnt+cnttt-100000);
                money -= cnttt*a[i];
                cnt+=cnttt;
            }
        }
        if(cnt&&a[i]>a[i-1]&&a[i]>a[i+1]){
            money += cnt*a[i];
            cnt = 0;
        }
    }
    printf("%lld\n",money);
}

E - Exponial

题意:不说了,很好读

分析:欧拉降幂

代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define lowbit(x) (x&(-x))
#define mem(a,b) memset(a,b,sizeof(a))
#define FRER() freopen("in.txt","r",stdin);
#define FREW() freopen("out.txt","w",stdout);
using namespace std;
const int maxn = 1e6 + 7 , maxch = 26 + 7;
ll phi(ll n){
    ll res = n;
    ll m = (ll)sqrt(n+0.5);
    for(int i=2;i<=m;i++){
        if(n%i==0){
            res = res/i*(i-1);
            while(n%i==0)n/=i;
        }
    }
    if(n>1) res = res /n *(n-1);
    return res;
}
ll q_pow(ll a,ll b,ll mod){
    ll res = 1;
    while(b){
        if(b&1) res = res * a % mod;
        a = a*a % mod;
        b >>= 1;
    }
    return res;
}
ll exponial(ll x,ll mod){
    if(mod==1) return 0;
    if(x==1) return 1;
    if(x==2) return 2%mod;
    if(x==3) return 9%mod;
    if(x==4) return 262144%mod;
    ll mmod = phi(mod);
    ll b = exponial(x-1,mmod);
    return q_pow(x,b+mmod,mod);
}
int main()
{
    ll n,m;
    scanf("%lld%lld",&n,&m);
    printf("%lld\n",exponial(n,m));
}

F - Fleecing the Raffle

题意: 现有一种抽奖活动,箱子内已经有n张票 不放回的抽奖p次 你可以往箱子内放任意多张票。 问你被抽中一次的概率最大是多少。

假设放入a张票概率公式就是 
a*(n+1-p到 n+a-p的连乘)/(n+1到n+a的连乘) n和p最多只有1e6 

暴力枚举即可

代码:
 

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define lowbit(x) (x&(-x))
#define mem(a,b) memset(a,b,sizeof(a))
#define FRER() freopen("in.txt","r",stdin);
#define FREW() freopen("out.txt","w",stdout);
using namespace std;
const int maxn = 100000 + 7;
double n,p,x;
int main(){
    cin>>n>>p;
    double Max , ans;
    Max = ans = 1.0*p/(n+1);
    for(double i=2;;i++){
        ans = ans * i / (i-1) * (n-p+i) / (n+i);
        if(Max>ans) break;
        Max = ans;
    }
    printf("%.8f\n",Max);
}

G - Game Rank

题意:游戏模拟

分析:模拟即可

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int up[30]=
{
    5,5,5,5,5,5,5,5,5,5,5,
    4,4,4,4,4,
    3,3,3,3,3,
    2,2,2,2,2
};
int main()
{
    //freopen("i.txt","r",stdin);
    string s;
    cin>>s;
    int level=25,cnt=0,star=0;
    bool flag=false;
    for(int i=0; i<s.length(); i++)
    {
        if(s[i]=='W')
        {
            star++;
            cnt++;
            if(cnt>=3&&level>=6)
            star++;
        }
        if(s[i]=='L')
        {
            cnt=0;
            if(level<=20)
            star--;
            if(level==20&&star<0)
            star=0;
            if(level<20&&star<0)
            {
                level++;
                star=up[level]-1;
            }
        }
        if(star>up[level])
        {
            star=star-up[level];
            level--;
        }
        if(level<1)
        {
            flag=true;
            break;
        }
    }
    if(flag)
        cout<<"Legend"<<endl;
    else cout<<level<<endl;
}

H - Highest Tower

 I - Interception

J - Jumbled Compass

题意:一个圆周,给出一个起点度数,一个终点度数,问顺时针和逆时针转到角度最小的是哪一个

分析:签到题。

代码:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
int main()
{
    //freopen("i.txt","r",stdin);
    int a,b;
    scanf("%d%d",&a,&b);
    int now=9999;
    for(int i=-179;i<=180;++i)
    {
        if((a+i+360)%360==b&&abs(i)<abs(now))now=i;
    }
    printf("%d\n",now);
}

K - Keeping the Dogs Apart

  

猜你喜欢

转载自blog.csdn.net/Insist_77/article/details/83116715