GDUFE 2017 2th Monthly

本文解析了1000至1010号算法题目的解题思路与代码实现,包括字符串处理、数据结构、动态规划等多个方面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于输入输出:
同学们在使用OJ时,请看清楚题目描述。OJ判题时,是将测试数据做为标准输入输入的,你的程序需要输出每一组输入的结果。大部分题目都要求输入到文件末尾,也就是说,你的读入也应当读取到文件末尾,并输出读取到的所有数据的结果。每一组数据都一定满足题目给出的规范,无需进行特判。也不要输出除了所需结果外的任何字符。

关于WA:
出现Wrong Answer时,请思考自己的程序复杂度及解法的正确性。一般来说,系统的测试数据不会出错(虽然这次就错了囧),当怀疑系统的正确性时,请在Clarification中提出疑问。

1000.
枚举插入的位置及插入的字母,之后统计新字符串的结果即可。
时间复杂度为 O(9n2)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
//
typedef long long ll;

char cz[110],tz[110];int n;

int _cl(int n){
    int i,rs=0;for(i=0;i+2<n;++i)if(tz[i]=='A'&&tz[i+1]=='C'&&tz[i+2]=='M')++rs;
    return rs;
};

bool cl(){
    int i,j,k,t;if(scanf("%s",cz)==-1)return 0;n=strlen(cz);
    for(i=0,j=0;i<n+1;++i){
        for(k=0;k<i;++k)tz[k]=cz[k];
        for(++k;k<n+1;++k)tz[k]=cz[k-1];
        tz[i]='A';j=max(j,_cl(n+1));
        tz[i]='C';j=max(j,_cl(n+1));
        tz[i]='M';j=max(j,_cl(n+1));
    }
    printf("%d\n",j);
    return 1;
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(cl());
    return 0;
};

1001.
空间没限制,直接用map硬上即可,复杂度 O(nlogn)
PS.有 O(n) 的解法。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<map>
//
typedef long long ll;
typedef pair<int,int>pi;

bool cl(){
    int n,i,j,k,a;map<int,int>mp;
    if(scanf("%d",&n)==-1)return 0;
    n=n*2+1;
    for(j=0;n;n--){
        scanf("%d",&a);
        map<int,int>::iterator it=mp.find(a);
        if(it==mp.end())mp.insert(pi(a,1));
        else
            j=max(j,++it->second);
    }
    map<int,int>::iterator it=mp.begin();
    for(;it!=mp.end();++it)if(it->second==j){
        j=it->first;
        break;
    }
    printf("%d\n",j);
    return 1;
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(cl());
    return 0;
};

1002.
一个模板题,对于每一个点对,求出他们的LCA(最近公共祖先),结果为 dis(s,lca)+dis(t,lca)
先固定根,dfs预处理出每个点的深度,则 dis(a,b)=|dep(a)dep(b)| ,用倍增 O(nlogn+qlogn) 可以通过。
也可以用Tarjan离线把复杂度中的 log 去掉。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<map>
//
typedef long long ll;
typedef pair<int,int>pi;

int fa[18][40010],dep[40010];
struct eg{int u,v,nx;}gp[80010];int cnt,hd[40010];

void psh(int u,int v){
    ++cnt;gp[cnt].u=u,gp[cnt].v=v,gp[cnt].nx=hd[u],hd[u]=cnt;
};
int tds(int v,int d){
    int i;for(i=0;d;++i,d>>=1)if(d&1)v=fa[i][v];
    return v;
};
int lca(int a,int b){
    if(dep[a]<dep[b])b=tds(b,dep[b]-dep[a]);
    else
        a=tds(a,dep[a]-dep[b]);
    if(a==b)return a;
    int i;for(i=17;i>=0;--i)if(fa[i][a]!=fa[i][b])
        a=fa[i][a],b=fa[i][b];
    return fa[0][a];
};

void dfs(int v,int f,int d){
    dep[v]=d;int i;for(i=hd[v];i;i=gp[i].nx)if(gp[i].v!=f)
        dfs(gp[i].v,v,d+1),fa[0][gp[i].v]=v;
};
void init(int n){
    int i,j;for(i=1;i<=17;++i)for(j=1;j<=n;++j)
        fa[i][j]=fa[i-1][fa[i-1][j]];
};

bool cl(){
    int i,j,k,n,q,a,b;if(scanf("%d",&n)==-1)return 0;
    for(cnt=0,clr(hd),clr(fa),i=1;i<n;++i){
        scanf("%d %d",&a,&b);psh(a,b),psh(b,a);
    }
    dfs(1,0,0);scanf("%d",&q);init(n);
    for(;q;q--){
        scanf("%d %d",&a,&b);
        int t=lca(a,b);
        printf("%d\n",dep[b]-dep[t]+dep[a]-dep[t]);
    }
    return 1;
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(cl());
    return 0;
};

1003.
枚举因数,预处理出每个数的因子数,再将满足要求的数置为1,其余置为0,求前缀和,即可在 O(1) 下计算每个查询(任意一个区间都可以表示为两个前缀相减)。
预处理因子的时间复杂度为: O(ni=1ni)=O(nlogn)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<map>
//
typedef long long ll;
typedef pair<int,int>pi;
const int up=500000;
int ar[up+1];

void init(){
    int i,j;clr(ar);for(i=1;i<=up;++i){
        for(j=i;j<=up;j+=i)ar[j]++;
    }
    for(i=1;i<=up;++i)ar[i]=ar[i]==4?1:0;
    for(i=1;i<=up;++i)ar[i]+=ar[i-1];
};

void cl(){
    int a,b;scanf("%d %d",&a,&b);printf("%d\n",ar[b]-ar[a-1]);
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    init();int t;scanf("%d",&t);while(t--)cl();
    return 0;
};

1004.
直接求乘积很显然会爆double,容易想到将每个数取对数,则有 ln(AB)=ln(A)+ln(B)
枚举区间求和比较即可。
时间复杂度为 O(n)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<map>
//
typedef long long ll;

double ar[1010];int n;

bool cl(){
    int i,j,k;if(scanf("%d %d",&n,&k)==-1)return 0;
    for(i=1;i<=n;++i){
        scanf("%d",&j);
        ar[i]=log((double)j);
    }
    double rs=0;
    for(i=1;i<=k;++i){
        rs+=ar[i];
    }
    double mx=rs;int ts=1;
    for(i=2;i+k-1<=n;++i){
        rs=0;for(j=i;j<=i+k-1;++j)rs+=ar[j];
        if(rs>mx)
            mx=rs,ts=i;
    }
    printf("%d\n",ts);
    return 1;
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(cl());
    return 0;
};

1005.
恶俗暴力题,枚举所有的符号及括号组合即可。
括号组合如下:
ABCD,(AB)(CD),A(B(CD)),A(BC)D,A((BC)D)
注意到每个数可以调换顺序,故我们预处理出所有 104 种排列,并计算是否满足要求。同时记录满足要求的序列的递增序列,即我们将序列排序得到他们的最小表示,之后将这个序列转换给11进制数存储。
对每个查询,计算序列的最小表示,在表中查询即可。
总共可能的方案数为 104345
注意在比较中需要处理精度问题。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<set>
#include<map>
//
typedef long long ll;
#define x(a,b,c) (_cl(ar[a],ar[b],c))
int ar[4],dr[3];bool bd[15000];
double _cl(double a,double b,int x){
    return !x?a+b:x==1?a-b:x==2?a*b:a/b;
};
bool _ck(double x){return fabs(x-24.0)<=1e-6;};
bool ck(){
    int i,j,k;for(i=0;i<4;++i)for(j=0;j<4;++j)for(k=0;k<4;++k){
        dr[0]=i,dr[1]=j,dr[2]=k;
        if(_ck(_cl(_cl(x(0,1,dr[0]),ar[2],dr[1]),ar[3],dr[2])))return 1;
        if(_ck(_cl(x(0,1,dr[0]),x(2,3,dr[2]),dr[1])))return 1;
        if(_ck(_cl(ar[0],_cl(ar[1],x(2,3,dr[2]),dr[1]),dr[0])))return 1;
        if(_ck(_cl(_cl(ar[0],x(1,2,dr[1]),dr[0]),ar[3],dr[2])))return 1;
        if(_ck(_cl(ar[0],_cl(x(1,2,dr[1]),ar[3],dr[2]),dr[0])))return 1;
    }
    return 0;
};
void init(){
    int i,j,k,d;for(clr(bd),i=1;i<=10;++i)for(j=1;j<=10;++j)for(k=1;k<=10;++k)for(d=1;d<=10;++d){
        ar[0]=i,ar[1]=j,ar[2]=k,ar[3]=d;
        if(ck()){
            sort(ar,ar+4);bd[ar[0]+ar[1]*11+ar[2]*121+ar[3]*1331]=1;
        }
    }
};

void cl(){
    int i;for(i=0;i<4;scanf("%d",&ar[i++]));sort(ar,ar+4);
    printf("%s\n",bd[ar[0]+ar[1]*11+ar[2]*121+ar[3]*1331]?"YES":"NO");
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;scanf("%d",&t),init();while(t--)cl();
    return 0;
};

1006.
可以发现,第一次必然要将一个饼放到小盘上,之后可以从其他饼中不断的填补之前产生的空缺。这样每个饼都要往外拿 n1 次,最后我们将小盘上的饼放到空缺中即可。
答案为 n(n1)+1

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<map>
//
typedef long long ll;

bool cl(){
    int n;if(scanf("%d",&n)==-1)return 0;
    if(n==1){printf("0\n");return 1;}
    printf("%d\n",n*(n-1)+1);
    return 1;
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(cl());
    return 0;
};

1007.
计算几何,不存在的。百度“点到线段最短距离”即可。
1008.
考虑用dp解决,设 f(n,k) 表示有 n 块地待挖,剩余k个铲子时的结果。
每次枚举挖第 i 块,则:
爆炸时,有f(i1,k1)+1
否则,有 f(ni,k)+1
那么,有转移方程:
f(n,k)=MINni=1{MAX{f(ni,k),f(i1,k1)}+1}
时间复杂度为 O(n2k)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<map>
//
typedef long long ll;

int dp[101][21];

void init(){
    int i,j,k;for(clr(dp),i=1;i<=100;++i)dp[i][1]=i;
    for(i=1;i<=20;++i)dp[1][i]=1;
    for(i=2;i<=100;++i)for(j=2;j<=20;++j){
        dp[i][j]=21;
        dp[i][j]=dp[i-1][j]+1;
        dp[i][j]=min(dp[i][j],dp[i-1][j-1]+1);
        for(k=2;k<i;++k){
            dp[i][j]=min(dp[i][j],max(dp[i-k][j],dp[k-1][j-1])+1);
        }
    }
};

bool cl(){
    int n,k;if(scanf("%d %d",&n,&k)==-1)return 0;
    printf("%d\n",dp[n][k]);
    return 1;
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    init();while(cl());
    return 0;
};

1009.
直接模拟即可。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<map>
//
typedef long long ll;

char mt[10][10]={
    "11111100",
    "01100000",
    "11011010",
    "11110010",
    "01100110",
    "10110110",
    "10111110",
    "11100000",
    "11111110",
    "11110110"
},cz[10];

ll _cl(int d){
    ll r=0,t=1;int i;for(i=0;i<8;++i,t<<=1)if(mt[d][i]=='1')
        r+=t;
    return r;
};

bool cl(){
    int i,j;if(scanf("%s",cz)==-1)return 0;
    ll rs=_cl(cz[4]-'0');
    rs+=(_cl(cz[3]-'0')<<8);
    rs+=(_cl(cz[1]-'0')<<16);
    rs+=(_cl(cz[0]-'0')<<24);
    printf("%lld\n",rs);
    return 1;
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(cl());
    return 0;
};

1010.
考虑使用Polya定理解决。
容易发现翻转操作等价于连续两次平行边的扭转,所以不用考虑。
注意到仅仅使用扭转操作,产生的置换个数是 O(4!) ,非常的少。
在本机暴力包含扭转与旋转的所有循环节后,使用Polya定理即可。
答案为 20n4+21n8+6n12+n1648
暴力的代码也在下面。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<set>
#include<map>
//
typedef long long ll;
#define w(a,b) swap(a,b)
#define e(a,b) x[a]=t[b]
set<string>st;
void r0(int*t){w(t[6],t[10]),w(t[12],t[2]),w(t[14],t[5]),w(t[16],t[9]);};
void r1(int*t){w(t[3],t[7]),w(t[1],t[11]),w(t[13],t[4]),w(t[15],t[8]);};
void r2(int*t){w(t[3],t[6]),w(t[1],t[2]),w(t[16],t[4]),w(t[15],t[5]);};
void r3(int*t){w(t[7],t[10]),w(t[11],t[12]),w(t[14],t[8]),w(t[13],t[9]);};
void rot(int*t){
    int x[17];
    e(3,2),e(7,1);e(1,6),e(2,10);
    e(11,3),e(12,7);e(6,12),e(10,11);
    e(8,4),e(9,8),e(5,9),e(4,5);
    e(15,16),e(16,14),e(14,13),e(13,15);
    memcpy(t,x,17*sizeof(int));
};

int dr[17],sm[17];
void cd(string&a,int x){
    for(;x;x/=10)a.push_back(char(x%10+'0'));
};
string xd(int*r){
    int i;string a;for(i=1;i<=16;cd(a,dr[i++]));
    return a;
};
int _cl(int*t){
    int i,j,r=0;bool bd[17];for(clr(bd),i=1;i<=16;++i)if(!bd[i]){
        for(j=i,++r;!bd[j];j=t[j])bd[j]=1;
    }
    return r;
};
void dfs(){
    int i,j,k,t[17];memcpy(t,dr,17*sizeof(int));
    string s=xd(dr);if(st.find(s)!=st.end())return;st.insert(s);
    sm[_cl(dr)]++;
    r0(dr);dfs();memcpy(dr,t,17*sizeof(int));
    r1(dr);dfs();memcpy(dr,t,17*sizeof(int));
    r2(dr);dfs();memcpy(dr,t,17*sizeof(int));
    r3(dr);dfs();memcpy(dr,t,17*sizeof(int));
    rot(dr);dfs();memcpy(dr,t,17*sizeof(int));
};

ll qni(ll a,ll b){
    ll r=1;for(;b;b>>=1,a*=a)if(b&1)r*=a;
    return r;
};

bool cl(){
    int n;if(scanf("%d",&n)==-1)return 0;
    ll rs=qni(n,4)*20+qni(n,8)*21+qni(n,12)*6+qni(n,16);
    printf("%lld\n",rs/48);
    return 1;
};

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(cl());
    return 0;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值