【第八届河南理工大学程序设计大赛(正式赛)】 BDE F GHIJKLM

A 接竹竿

B 目录地址
分析: 很有意思的题目,记录下吧。当时读了前半段的题目,感觉不是很好写,读完输入输出,发现完全和前半部分没有关系, m次操作中,给定的就是一个线性的顺序,我们直接模拟就好了啊。

#include
using namespace std;
const int MAXN = 1e5 ;

vector<string>ve;
int main(){
    int n,m;
    while(cin>>n>>m){
        ve.clear();
        while(n--) { string a,b; cin>>a>>b; }

        ve.push_back("C/");
        while(m--){
            string op; cin>>op;
            if(op=="see"){
                string ans=ve.back();
                cout<<ans<<endl;
            }else {
                string x; cin>>x;
                if(x!=".."){
                    x+="/";
                    string t=ve.back();  t+=x;
                    ve.push_back(t);
                }else {
                    if(ve.size()==0) continue;
                    ve.pop_back();
                }
            }
        }
    }
return 0;
}

C逃出厂房

D疯狂精灵球
看懂题,模拟。

#include
#include
#include
using namespace std;
const int MAXN = 1e5 + 10;
set <int> s[MAXN];
int ma[MAXN];
int main()
{
    int x,y,N,M,T;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&N,&M); 
        for(int i = 1; i <= MAXN; i++)
            s[i].clear();
        for(int i = 1 ; i <= N; i++)
            scanf("%d",&ma[i]);
        while(M--){
            scanf("%d %d",&x,&y);
            if(ma[x] == ma[y]) continue;
            s[ma[x]].insert(ma[y]);
            s[ma[y]].insert(ma[x]);
        }
        int cut = ma[1];
        for(int i = 2 ; i <= N ; i++){
            if(s[cut].size() < s[ma[i]].size())
                cut = ma[i];
            else if(s[cut].size() == s[ma[i]].size() && cut < ma[i])
                cut = ma[i];
        }
        printf("%d\n",cut);
    }
    return 0;
}

E 文件统计
本想着是树链剖分,点权更新+路径求和。 其实不用,找一下规律就会发现,对于一个节点更新之后,只会影响其子树上的值,发现这一点之后就好弄了。
树状数组+dfs序
树状数组来控制 区间加减+单点查询
dfs序来维护以某个点为根的子树的 区间 。
代码

#include
using namespace std;
const int MAXN = 23333+11 ;
#define LL long long

struct BIT{
    int n;
    LL c[MAXN<<1];
    inline int lowbit(int x){return x&(-x);}
    void init(){ memset(c,0,sizeof(c)); }
    void add(int x,int val){
        for(int i=x;i<=n;i+=lowbit(i))
            c[i]+=val;
    }
    LL sum(int x){
        LL ans=0;
        for(int i=x;i>0;i-=lowbit(i))
            ans+=c[i];
        return ans;
    }
}bit;

LL num[MAXN+2]; vector<int>ve[MAXN+2];
void init(int n){
    bit.n=n; bit.init();
    for(int i=0;i<=n;i++) ve[i].clear();
    memset(num,0,sizeof(num));
}
int in[MAXN+2],out[MAXN+2],id;
void getmap(int m){
    while(m--){
        int a,b;
        scanf("%d%d",&a,&b);
        ve[a].push_back(b);
        ve[b].push_back(a); // 汗TAT 有毒把,非要是双向边才可以。
    }
}

void dfs(int now,int pre){
    in[now]=++id;
    for(int i=0;i<ve[now].size();i++){
        int v=ve[now][i];
        if(v!=pre) dfs(v,now);
    }
    out[now]=id;
}
int n,m;
void solve(int qq){
    id=0; dfs(1,-1);
    //for(int i=1;i<=n;i++){ printf("%d %d \n",in[i],out[i]); }
    char op[10];int x;
    while(qq--){
        scanf("%s %d",op,&x);
        if(op[0]=='q') printf("%lld\n",bit.sum(in[x]));
        else{
            int y; scanf("%d",&y);
            if(op[0]=='a') {
                num[x]+=y;
                bit.add(in[x],y);
                bit.add(out[x]+1,-y);
            }else{
                if(num[x]<y) puts("error");
                else {
                    y=-y;
                    num[x]+=y;
                    bit.add(in[x],y);
                    bit.add(out[x]+1,-y);
                }
            }
        }
    }
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        init(n);
        getmap(n-1);
        solve(m);
    }
return 0;
}

F GCD与LCM
题目描述
Ocean某天遇到了一个很简单的数学题,但是他的大脑已经超负荷了。现在请你帮帮他吧:

已知D = GCD(x, y) + LCM(x, y),求合法组合(x, y)总数,其中x > 0且y > 0。

输入
第一行输入一个整数T,代表有T组测试数据。

接下来每行输入一个整数D。

注:1<=T,D<=1051<=T,D<=105。

输出
对每组测试数据,输出一个整数代表可能出现的组合数。

样例输入
3
2
3
4
样例输出
1
2
3
提示
GCD:最大公约数

LCM:最小公倍数

样例中:

若D=2,可能有①x=1,y=1;

若D=3,可能有①x=1,y=2②x=2,y=1

若D=4,可能有①x=1,y=3②x=2,y=2③x=3,y=1

分析: 今天想了许久,终于a掉。 其实最后化简的式子,和当时比赛时候化简的式子一样,不过有个地方没有弄好,结果当时怎么都过不了。
D = L + G 。
则 x = G * a . y = G * b .
x * y = G * G * a * b
x * y = G * L .
联立上式子: a * b = L / G = (D-G) / G = D/G -1
关键式子 : a * b = D / G -1 (注意这里D/G 一定是整数(因为L/G 一定是整数),同时a和b一定是互质的(如果不是互质的话, 就不能够满足G为最大公因数),比赛时就是这里没有想通 )
就是针对这个式子,开始化简,减少时间复杂度。
令 a * b = z .
z = D/ G - 1 .对于式子的右边值,我们可以遍历1-sqrt(D), 关键是 知道了a * b的值z,之前已经用了sqrt的时间复杂度,接下来的只能够用O(1)的了,我们怎么用o(1)处理 有多少对互质的数相乘为z呢? 这里想起来之前写的 一道题,可以o(n)预处理求出n以内任意数的 因子个数 ,感觉可以从这个筛法中取得 。
看代码吧 : 其实挺短的代码 。 时间复杂度 o(T*sqrt(D)) .学长说标程是o(T * log(D)) 的,目前还真是想不出来了。 标程出来了,再贴。

600ms

#include
using namespace std;
const int MAXN = 1e5+11;
#define LL long long
 
LL gcd(LL a,LL b){ return b==0?a:gcd(b,a%b); }
int num[MAXN+2];// num[i] 表示a*b=i的种类数
void init(){
    num[1]=1;
    for(LL i=1;i*i<=MAXN;i++){
        for(LL j=i;j*i<=MAXN;j++){
            if(i==j) continue;
            if(gcd(i,j)==1) num[i*j]+=2; // 加上互质的条件
        }
    }
   // for(int i=1;i<=20;i++){
   //     printf("i==%d %d\n",i, num[i]);
   // }
}
LL solve(LL n){
    LL ans=0;
    for(LL i=1;i*i<=n;i++) {  //sqrt(D) 枚举z 
        if(n%i==0){
            //printf("i==%lld n=%lld \n",i,n);
            ans+=num[n/i-1];
            if(n/i!=i) ans+=num[i-1];
        }
    }
    return ans;
}
 
int main(){
    init();
    int T;scanf("%d",&T);
    while(T--){
        LL n;scanf("%lld",&n);
        if(n==1) puts("0");
        else printf("%lld\n",solve(n));
    }
return 0;
}
 

优化一下上述代码,在筛num[]的时候发现同时也可以将因子一并筛出来。
240ms

#include
using namespace std;
const int MAXN = 1e5+11;
#define LL long long

LL gcd(LL a,LL b){ return b==0?a:gcd(b,a%b); }
int num[MAXN+2];
int fac[MAXN+2][250],cnt[MAXN+2];
void init(){
    num[1]=1; num[0]=0;
    for(LL i=1;i*i<=MAXN;i++){// 这里可以同时筛选 两个
        for(LL j=i;j*i<=MAXN;j++){
            fac[i*j][cnt[i*j]++]=j;
            if(i==j) continue;
            if(gcd(i,j)==1) num[i*j]+=2;
        }
    }
}

LL solve(int n){
    LL ans=0;
    for(int i=0;i<cnt[n];i++){
        int G=fac[n][i];
        ans+=num[n/G-1];
        if(n/G!=G) ans+=num[G-1];
    }
    return ans;
}

int main(){
    init();
    int T;scanf("%d",&T);
    while(T--){
        int n;scanf("%d",&n);
        if(n==1) puts("0");
        else printf("%lld\n",solve(n));
    }
return 0;
}

分析二: 标程: 对于一个数字n来说,其可以拆分为a*b=n (a,b 互质)个数为 2^m(m为数n的质因子个数),从算数基本定理来考虑就行了。
一开始用了vector来存因子,然后枚举,400ms左右,后来换为普通数组模拟存因子,200ms左右 ,TAT ~

#include
using namespace std;
const int MAXN = 1e5+11;
#define LL long long

bool su[MAXN+2];int fac[MAXN+2][250],cnt[MAXN+2];int num[MAXN+2];
LL F[35];  // 数n的 质因子个数为Log(n)
void init(){  // 预处理都是o(n)
    F[0]=1; for(LL i=1;i<=31;i++) F[i]=F[i-1]*2;

    su[0]=su[1]=1;
    for(int i=2;i<=MAXN;i++){
        if(!su[i]) {
            num[i]=1;for(int j=2*i;j<=MAXN;j+=i){
                su[j]=1;
                num[j]++;
            }
        }
    }

    for(int i=1;i*i<=MAXN;i++){
        for(int j=i;j*i<=MAXN;j++){
            fac[i*j][cnt[i*j]++]=i;
            if(i!=j) fac[i*j][cnt[i*j]++]=j;
            
            //fac[j*i].push_back(j); 
            //if(i!=j) fac[i*j].push_back(i);
        }
    }
}

LL solve(int n){
    LL ans=0;
    for(int i=0;i<cnt[n];i++){
        int t=n/fac[n][i]-1;
        if(t>=1) ans+=F[num[t]];
    }
    return ans;
}

int main(){
    init();
    int T;scanf("%d",&T);
    while(T--){
        int n;scanf("%d",&n);
        printf("%lld\n",solve(n));
    }
return 0;
}

G: 杨八方的表面兄弟
分析: 很容易知道 ans = C(n,2)+C(n,3)+C(n,4)+C(n,n) .
由二项式定理容易知道 C(n,0)+C(n,1)+…C(n,n) = 2^n .
综上 ans = 2^n -1 -n .
最后取模这里 好多人容易错。
ans = ((2^n%mod) - 1 - n +mod ) % mod 。
百度的同余定理(a-b)%mod = (a%mod - b%mod )%mod ,当然是对的,但是对于做题来说,输出的都是>=0的值,为了防止最后值是负数,所以要先+mod 最后再mod掉, 这样就可以很好的避免。

H 杨八方的商业互吹
并查集随便写

#include
#include
using namespace std;
int a[1000020],par[1000020],ran[1000020],cnt[1000020];
int find(int m) {
    if(m==par[m])
        return m;
    else
        return par[m]=find(par[m]);
}
void unite(int x,int y) {
    x=find(x);
    y=find(y);
    if(x==y)
        return;
    par[y]=x;
}
int main() {
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1; i<=n; i++) {
        scanf("%d",&a[i]);
    }
    for(int i=1; i<=n; i++) {
        par[i]=i;
        cnt[i]=1;
        ran[i]=a[i];
    }
    while(m--) {
        int x,y;
        scanf("%d %d",&x,&y);
        unite(x,y);
    }
    for(int i=1; i<=n; i++) {
        int m=find(i);
        if(m!=i) {
            ran[m]+=a[i];
            cnt[m]++;
            cnt[i]=0;
        }
    }
    double res=0;
    for(int i=1; i<=n; i++) {
        if(cnt[i]) {
            res=max(res,1.0*ran[i]/cnt[i]);
        }
    }
    printf("%.2lf\n",res);
    return 0;
}

I 杨八方的编译原理
不难,当时没有时间写了。
模拟就好了。

#include
using namespace std;
#define LL long long
const int MAXN = 1e5;
const int MAXM =  1e6 ;
 
map<string,int>vis; 
int getval(string s){ // 是否为变量,并获取其值。
    bool flag=0; int ans=0;
    for(int i=0;s[i];i++) {
        if(s[i]<'0'||s[i]>'9') { flag =1; break;}
        else
            ans=ans*10+s[i]-'0';
    }
    if(flag) return vis[s];
    return ans;
}
int main(){
    int T ;cin>>T;
    while(T--){
        int ans;  string s;  cin>>s;
        if(s=="show"){
            string op; cin>>op;
            ans=0;  string t=""; string pre="+";
            for(int i=0;op[i];i++){
                if(op[i]!='-'&&op[i]!='+') t+=op[i];
                else {
                    if(pre=="+") ans+=getval(t);
                    else if(pre=="-") ans-=getval(t);
                    pre=""; t=""; pre+=op[i];
                }
            }
            if(pre=="+") ans+=getval(t);
            else if(pre=="-") ans-=getval(t);
            cout<<ans<<endl;
        }else {
            string t=""; string st=""; string pre="+";
            for(int i=0;s[i];i++){
                if(s[i]!='-'&&s[i]!='+'&&s[i]!='=')  t+=s[i];
                else{
                    if(s[i]=='=') { st=t; ans=0; }
                    else {
                        if(pre=="+") ans+=getval(t);
                        else if(pre=="-") ans-=getval(t);
                        pre=""; pre+=s[i];
                    }
                    t="";
                }
            }
            if(pre=="+") ans+=getval(t);
            else if(pre=="-") ans-=getval(t);
            vis[st]=ans ;
        }
    }
return 0;
}

J 杨八方的英雄联盟
简单的二分,但是比赛的时候没有看 ,看到几何还有这么长的体面,没敢看下去,噗。真是~ 。

//package FirstTime;
import java.util.*;
import java.math.*;
import java.text.DecimalFormat;

public class Main{
	static double R,H,a,b,c;
	static double GetDis(double x1,double y1,double x2,double y2){
		return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
	}
	static boolean f(double s){
		return a*s*s+b*Math.sqrt(s)+c-H<0;
	}
	static void solve(double le,double ri) {// 求导后发现 恒为正,单调递增的。
		double ans = -1.0; 
		while(ri-le>1e-8){
			double mid = (le + ri) / 2; 
			if(f(mid)) le = mid ; 
			else {
				ans = mid ; ri = mid ;
			}
		}
		DecimalFormat df = new DecimalFormat("0.00");
		if(ans==-1.0) System.out.println(-1);
		else System.out.println(df.format(ans));
	}
	public static void main(String[] agrs){
		Scanner cin = new Scanner(System.in);  
		int T;T=cin.nextInt();
		while(T--!=0){
			double x1,x2,y1,y2;
			R=cin.nextDouble(); x1=cin.nextDouble(); y1=cin.nextDouble(); 
			H=cin.nextDouble(); x2=cin.nextDouble(); y2=cin.nextDouble();
			a=cin.nextDouble(); b=cin.nextDouble();  c=cin.nextDouble();
			double d=GetDis(x1,y1,x2,y2);
			double l=Math.abs(d-R),r=d+R;
			solve(l,r);
		}
	}
}

K. 杨八方的绝地求生
签到

#include 
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
    int t;
    double v,x1,y1,r,x2,y2;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lf%lf%lf",&v,&x1,&y1);
        scanf("%lf%lf%lf",&r,&x2,&y2);
        double dis,a,b;
        a=(x2-x1)*(x2-x1);
        b=(y2-y1)*(y2-y1);
        dis=sqrt(a+b);
        if(dis<=r)
            printf("0.0\n");
        else
            printf("%.1lf\n",(dis-r)*1.0/v);
    }
    return 0;
}

L. 杨八方的魔力占卜
签到

M: 得分期望
分析: 看题目意思,就是遍历序列,然后求当前值和其余值的异或值>=M的个数,看数据范围时间复杂度只能够为T * N * log(N) .
对于异或的处理,尤其是这种区间形式的异或,可以用01Trie ,01Trie的最典型的应用就是区间异或最大值,针对情况可能要用持久化的01Trie 。 这道题要进行一些变形 ,但是思想是一样的(从最高位贪心取值)。
看代码
在最基本的01Trie操作(插删查)的基础上进行简单的应用。

#include
using namespace std;
#define  LL long long

const int MAXN = 10000+11;
const int MAXM = 1e6;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;

int ch[MAXN*32][2],sz;
int num[MAXN*32];
void init() {
    memset(ch[0],0,sizeof(ch[0]));
    sz=1;
    memset(num,0,sizeof(num));
}
void Insert(int a) {
    int u=0;
    for(int i=31; i>=0; i--) {
        int c=1&(a>>i);
        if(!ch[u][c]) {
            memset(ch[sz],0,sizeof(ch[sz]));
            ch[u][c]=sz++;
        }
        u=ch[u][c];
        num[u]++;
    }
}
void Delete(int a) {
    int u=0;
    for(int i=31; i>=0; i--) {
        int c=1&(a>>i);
        u=ch[u][c];
        num[u]--;
    }
}

int Query(int a,int m) {//重点 
    int u=0;
    int ans=0;
    for(int i=31; i>=0; i--) { // 从最高位来枚举,贪心思想
        int c=1&(a>>i);  int d=1&(m>>i);
        if(d==1){ //d=1的话,只能往c^1的方向走才能够保持最起码和m的值相等 
            if(ch[u][c^1]&&num[ch[u][c^1]]){
                u=ch[u][c^1];
            }else // 否则的话,c方向之后的值异或后肯定都小于m,可以返回ans了
                 return ans;

        }else{// d=0  
            if(ch[u][c^1]&&num[ch[u][c^1]]){ // 如果c^1的方向可以走,那么这条路之后的数异或后肯定都大于m,直接加上就行。
                ans+=num[ch[u][c^1]];
            }

            if(ch[u][c]&&num[ch[u][c]])// 如果c方向可以走,优先走这个方向,因为这条路上的值异或后才可能大于m 
                u=ch[u][c];
            else // 如果c的方向不能够走,那么c^1这条路的值异或后肯定大于m,而且前面算过了,可以返回ans了
                return ans;
        }
    }
    ans+=num[u];//剩下加的都是异或后等于m的
    return ans;
}

int arr[MAXN];
int main() {
   int T; scanf("%d",&T);
   while(T--){
        init();
        int n,m; scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) { scanf("%d",&arr[i]); Insert(arr[i]); }
        for(int i=1;i<=n;i++){
            Delete(arr[i]);
            int ge=Query(arr[i],m);
           // printf("ge = %d \n",ge);
            Insert(arr[i]);
            if(i>1) putchar(' ');
            printf("%.2lf",ge*1.0/(n-1));
        }
        puts("");
   }
    return 0;
}

你可能感兴趣的:(数学,gcd,+,lcm,+,exgcd,+,CRT,思维,模拟,字典树)