10.05 练习赛总结

比赛链接:点击这里传送


(赛中已过)A 题目链接:点击这里传送

在这里插入图片描述
题意:
给出两个数n和k,表示需要到达的人头数和总共有的钱。一开始now为0。每天可以花x元买x人( ∑ i = 1 m x i < k \sum_{i=1}^mx_i<k i=1mxi<k)。每天晚上会自动增加一些人头量,增加的量的公式为 m i n ( n o w , n − n o w 2 ) min(now,\frac{n-now}{2}) min(now,2nnow)。问最少需要多少天人头量能达到要求。
思路:
就第一天买1个人,然后让他每天晚上自己涨。涨到 k − 1 + n o w > = n k-1+now>=n k1+now>=n时就行了。因为这样白嫖的人最多,所以天数是最少的。
队友代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, k;

// yikaishi fang k ge


ll way5(){
    ll res = 100000;
    for (ll i=1;i<k;++i){
        ll sum = i, b = i, count = 1;
        sum = b + min(b, (n - b) / 2);
        b = sum;
        if (sum >= n)
        {
            return count;
        }
    while (n-sum > k-i && count <= 10000)
    {
        count++;
        sum = b + min(b, (n - b) / 2);
        b = sum;
    }
    if (sum >= n)
    {
        res = min(res, count);
    }
    else{
        res = min(res, count+1);
    }
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> k;
    if ((k+min(k, (n-k)/2)) >= n)
    {
        cout << 1 << endl;
        return 0;
    }
    ll res5 = way5();
    cout << res5 << endl;
    return 0;
}

(赛中已过)C 题目链接:点击这里传送

在这里插入图片描述
题意:
给出一棵树,将这棵树的节点放在一个1000000×20的网格中,每个格只能有一个节点,使得树边不相交(树边为线段),输出一种方案。
思路:
这个图比较特殊,长很大宽很小。所以要把重儿子放右边,轻儿子放上边。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
int k=1;
int n;
int x,y;
int head[MAXN],siz[MAXN],son[MAXN],father[MAXN],ans1[MAXN],ans2[MAXN];
struct edge
{
    int from,to,next;
}edge[MAXN*2];
void add_edge(int x,int y)
{
    edge[k].from=x;edge[k].to=y;edge[k].next=head[x];
    head[x]=k++;
}
void dfs(int s)
{
    siz[s]=1;
    son[s]=0;
    for(int i=head[s];i;i=edge[i].next)
    {
        if(edge[i].to!=father[s])
        {
            father[edge[i].to]=s;
            dfs(edge[i].to);
            siz[s]+=siz[edge[i].to];
            if(siz[edge[i].to]>siz[son[s]])
            {
                son[s]=edge[i].to;
            }
        }
    }
}

void dfss(int s)
{
    ans1[s]=x;ans2[s]=y;
    if(son[s]==0) return;
    y++;
    for(int i=head[s];i;i=edge[i].next)
    {
        if(edge[i].to!=son[s]&&edge[i].to!=father[s])
        {
            dfss(edge[i].to);
            x++;
        }
    }
    y--;
    x++;
    dfss(son[s]);
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<n;i++)
    {
        cin>>x>>y;
        add_edge(x,y);add_edge(y,x);
    }
    dfs(1);
    x=1;y=1;
    dfss(1);
    for(int i=1;i<=n;i++)
    {
        cout<<ans1[i]<<" "<<ans2[i]<<endl;
    }
    return 0;
}

(赛中已过)D 题目链接:点击这里传送

在这里插入图片描述
题意:
对整数进行编号:0编号为0,−1为1,1为2,即当x≥0时,编号为2x,当x<0时,编号为−2x−1。有一个数列 x i x_i xi(未知),将 x i x_i xi的编号化为128进制,然后除了最高位外,其余的数加128,按顺序给出操作后的序列,求原序列。
思路:
模拟。会在爆longlong上恶心人。
队友代码:

#include <bits/stdc++.h>
using namespace std;
#define ll unsigned long long

int n;
const int Max_n = 1e4 + 10;
ll a[Max_n];
ll bit[Max_n];
vector<ll> ve;
vector<char> ch;

void init(){
    bit[0] = 1;
    ll ans = 128;
    for (int i=1;i<=9;++i){
        bit[i] = bit[i-1]*ans;
    }
}

void solve(){
    cin >> n;
    for (int i=1;i<=n;++i){
        cin >> a[i];
    }
    for (int i=1;i<=n;++i){
        vector<ll> cur;
        int j = i;
        while (j<n && a[j]>=128){
            j++;
        }
        for (int k=i;k<=j;++k){
            cur.push_back(a[k]);
        }
        int clen = cur.size();
        ll ans = 0;
        for (int k=0;k<clen-1;++k){
            ans = ans + (cur[k]-128)*bit[k];
        }
        ans = ans + cur.back()*bit[clen-1];
        if (ans&1){
            ans = ans/2 + 1;
            ch.push_back('-');
        }
        else{
            ans = ans/2;
            ch.push_back('+');
        }

        ve.push_back(ans);
        i = j;
    }
    int vlen = ve.size();
    for (int i=0;i<vlen;++i){
        if (ch[i]=='-'){
            cout << ch[i] << ve[i];
        }
        else{
            cout << ve[i];
        }
        if (1){
            cout << endl;
        }
    }
}

int main(void){
    init();
    solve();
    return 0;
}

F 题目链接:点击这里传送

在这里插入图片描述
题意:
给出一张ACM赛制下封榜前的一张排行榜和封榜后的部分排行榜。验证封榜后那部分排行榜的可能性。
思路:
部分的终榜不是从第一名开始的,而是任意的,在这里被坑了亿会。
终榜中会少一些人,可能他们太强了到前面去了或者太弱了被挤下去了。
最优情况是在封榜的那一刻(240分钟)AK所有题目。
最坏情况是没做出题目。
然后进行模拟,将两种情况都与部分终榜的最高位和最低位进行比较,验证是否合法。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1005
#define ll long long
ll solvenum[MAXN];ll solvenum2[MAXN];//总过题数
ll solvetime[MAXN];ll solvetime2[MAXN];//总罚时
ll solvelast[MAXN];ll solvelast2[MAXN];//最后过题时间
char c[MAXN][30];char c2[MAXN][30];
ll num[MAXN][30];ll num2[MAXN][30];
ll tim[MAXN][30];ll tim2[MAXN][30];

string s[MAXN];
string s2[MAXN];
map <string,int> mp;
map <string,int> mp2;
ll n,m,k;
//需要注意部分终榜不一定是从1开始的
int pk(int x,int y)
{
    if(solvenum[x] > solvenum2[y]) return 1;
    else if(solvenum[x] < solvenum2[y]) return -1;
    if(solvetime[x] > solvetime2[y]) return -1;
    else if(solvetime[x] < solvetime2[y]) return 1;
    if(solvelast[x] > solvelast2[y]) return -1;
    else if(solvelast[x] < solvelast2[y]) return 1;
    else if(s[x] < s2[y]) return 1;  
    else return -1;
}

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>k>>n>>m;//题目数量,队伍的数量,封榜后
    for(int i=1;i<=n;i++)//封榜前的输入处理
    {
        cin>>s[i];
        mp[s[i]]=i;
        for(int j=1;j<=k;j++)
        {
            cin>>c[i][j]>>num[i][j]>>tim[i][j];
            if(c[i][j]=='+')//过了这题
            {
                solvenum[i]++;
                solvetime[i]+=(num[i][j]-1)*20+tim[i][j];
                solvelast[i]=max(solvelast[i],tim[i][j]);
            }

        }
    }

    for(int i=1;i<=m;i++)
    {
        cin>>s2[i];
        mp2[s2[i]]=i;
        for(int j=1;j<=k;j++)
        {
            cin>>c2[i][j]>>num2[i][j]>>tim2[i][j];
            if(c2[i][j]=='+')//封榜后过了这题
            {
                solvenum2[i]++;
                solvetime2[i]+=(num2[i][j]-1)*20+tim2[i][j];
                solvelast2[i]=max(solvelast2[i],tim2[i][j]);
            }
        }
    }

    if(m==1)
    {
        cout<<"Leaked"<<endl;
        return 0;
    }

    int f=1;
    for(int i=1;i<=n;i++)//
    {
        if(!mp2.count(s[i]))//如果在终榜找不到这个人,要么他太强了,要么他太弱了
        {
            int flag=0;
            if(pk(i,1)>0||pk(i,m)<0) flag=1;//在这之后未过题,被挤出了榜单
            //下面是封榜后开启厕所战神模式AK的情况
            for(int j=1;j<=k;j++)
            {
                if(c[i][j]=='-')//之前有罚时
                {
                    solvenum[i]++;
                    solvetime[i]+=(num[i][j])*20+240;//正好在封榜那一刻过题,巧吧
                    solvelast[i]=max(solvelast[i],(ll)240);
                }
                else if(c[i][j]=='.')//之前没有罚时
                {
                    solvenum[i]++;
                    solvetime[i]+=240;
                    solvelast[i]=max(solvelast[i],(ll)240);                   
                }
            }
            //模拟AK完了再进行比较
            if(pk(i,1)>0||pk(i,m)<0) flag=1;
            if(flag==0) f=0;

        }

    }
    if(f==1) cout<<"Leaked"<<endl;
    else cout<<"Fake"<<endl;
    return 0;
}

G 题目链接:点击这里传送

题面废话太多不看
题意:
给出一个n×m的网格图,网格图的每条边都有一个值。现在在每个格子中填一个整数,在格子的左、下边加x,右、上边减x。初始时网格的所有边都是0,问是否存在一种填数的方案使得最后所有边的值等于给出的边值。
思路:
解法一:
可以通过解方程得出这些中心气压值都是自由变量,及要么无解要么有无穷解。只需将任意中心气压值设为任意值后,根据规则将其他所有的中心气压值求出来,最后再根据规则验证推导出来的中心气压值的准确性。
代码:

#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 1005
#define ll long long
ll ans[1005][1005];
ll  r[1005][1005],c[1005][1005];//这是环
int m,n;
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++) 
	    {
            cin>>r[i][j];
            cin>>c[i][j];
        }
    } 
    //现在在每个格子中填一个整数x,在格子的左、下边加x,右、上边减x
    //一条水平的边值=上边气旋的气压值-下边气旋的气压值
    //一条竖直的边值=右边气旋的气压值-左边气旋的气压值
    ans[0][0]=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++) 
	    {
            ans[(i+n-1)%n][j]=ans[i][j]+r[i][j];//上
            ans[i][(j+m-1)%m]=ans[i][j]-c[i][j];//左
            ans[(i+1)%n][j]=ans[i][j]-r[(i+1)%n][j];//下
            ans[i][(j+1)%m]=ans[i][j]+c[i][(j+1)%m];//右
        }
    }
    
    int flag=1;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++) 
	    {
            //判断推出来的有没有自相矛盾
            if(ans[(i+n-1)%n][j]!=ans[i][j]+r[i][j])flag=0;
            if(ans[i][(j+m-1)%m]!=ans[i][j]-c[i][j])flag=0;
            if(ans[(i+1)%n][j]!=ans[i][j]-r[(i+1)%n][j])flag=0;
            if(ans[i][(j+1)%m]!=ans[i][j]+c[i][(j+1)%m])flag=0;
        }
    }
    
    if(flag==1) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
	return 0;
 } 

解法二:
队友的思路。
首先一开始所有的中心气压值肯定为0.所以对于一张图,如果是合法的话,那么他可以还原成初始全为0的状态。这就等价于这张图每行每列的边的和都为0(这里一直搞不懂)
代码:

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

int n, m;
const int Max_n = 500 + 10;
int a[Max_n][Max_n<<1];

ll read(){
    ll x = 0, f=1;char ch;
    do{ch = getchar();if (ch == '-') f=-1;}while(ch<'0' || ch>'9');
    do{x = x*10 + (ch-'0');ch = getchar();}while(ch>='0' && ch<='9');
    return x*f;
}

void solve(){
	n = read(), m = read();
	for (int i=1;i<=n;++i){
		for (int j=1;j<=m+m;++j){
			a[i][j] = read();
		}
	}
	ll suma = 0;
	for (int i=1;i<m+m;i+=2){
		for (int j=1;j<=n;++j){
			suma += a[j][i];
		}
		if (suma != 0){
			puts("No");
			return;
		}
	}
	for (int i=1;i<=n;++i){
		suma = 0;
		for (int j=2;j<=m+m;j+=2){
			suma += a[i][j];
		}
		if (suma != 0){
			puts("No");
			return;
		}
	}
	puts("Yes");
}

int main(void){
	solve();
	return 0;
}


H 题目链接:点击这里传送

在这里插入图片描述
题意:
存在一个序列ai,满足 a i ≥ 0 a_i≥0 ai0, a i − a i + 1 ≤ 1 a_i-a_{i+1}≤1 aiai+11,给出某些位置的数和一个数T,问是否存在一个序列 a i a_i ai,使得 ∑ a i = T ∑a_i=T ai=T
思路:
我们可以求出 ∑ a i \sum a_i ai的范围,判断T是否是不是在这个范围里面。
(建议)我的代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 200005
ll T,n,m;
ll a[MAXN];//记录的是pos
ll b[MAXN];//记录的是val

ll get_sum(ll x,ll y)//从x到y的温度总和(x<=y)
{
    if(x>y) return 0;
    //处理时x可能小于0,实际上不会小于0,求和时要把少的这部分加上去。
    if(y<0) return 0;
    ll error=0;
    if(x<0) error=-x;
    error=(error+1)*error/2;//这就是少的那部分(如果x为负数)
    return (x+y)*(y-x+1)/2+error;
}

bool check()
{
    ll max_sum=0;ll min_sum=0;//和的最值
    ll top=0;ll bot=0;//温度的峰值
    for(int i=1;i<=m;i++)
    {
        max_sum+=b[i];
        min_sum+=b[i];
    }
    //处理开头的温度(位置<b[1]的部分)
    top=b[1]+a[1]-1;//可能的最高温度
    bot=b[1]-(a[1]-1);//可能的最低温度
    max_sum+=get_sum(b[1]+1,top);//计算最高温度和
    min_sum+=get_sum(bot,b[1]-1);//计算最低温度和

    //处理结尾的温度(位置>b[m]的部分)
    top=b[m]+(n-a[m]);//可能的最高温度
    bot=b[m]-(n-a[m]);//可能的最低温度
    max_sum+=get_sum(b[m]+1,top);//计算最高温度和
    min_sum+=get_sum(bot,b[m]-1);//计算最低温度和

    for(int i=1;i<m;i++)//处理中间部分的温度
    {
        ll d_pos=a[i+1]-a[i];//位置的差值
        ll d_val=abs(b[i+1]-b[i]);//温度差的绝对值
        ll minx=min(b[i],b[i+1]);ll maxx=max(b[i],b[i+1]);
        if(d_val>d_pos)//温度变化太快,直接非法
        {
            return false;
        }
        //if(d_pos==1) continue;
        top=maxx+(d_pos-d_val)/2;//可能的温度最高值
        bot=minx-(d_pos-d_val)/2;//可能的温度最低值,这个值算出来可能小于0

        //累加时注意奇偶性 可能是1 2 3 3 2 1这种情况,也可能是1 2 1这种情况
        if(d_val%2==d_pos%2)//温度和得再减掉一个峰值
        {
            max_sum+=get_sum(maxx,top)+get_sum(minx,top)-max(0ll,top);
            min_sum+=get_sum(bot,minx)+get_sum(bot,maxx)-max(0ll,bot);
        }
        else
        {
            max_sum+=get_sum(maxx,top)+get_sum(minx,top);
            min_sum+=get_sum(bot,minx)+get_sum(bot,maxx);
        }

        max_sum-=(minx+maxx);//减掉重复计算的已知值
        min_sum-=(minx+maxx);//减掉重复计算的已知值
    }
    //cout<<"min_sum: "<<min_sum<<" "<<"max_sum: "<<max_sum<<endl;
    if(T>=min_sum&&T<=max_sum) return true;
    else return false;
}

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>T>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>a[i]>>b[i];
    }
    if(check()==false) cout<<"No"<<endl;
    else cout<<"Yes"<<endl;
    return 0;
}

痛苦调试两小时 队友代码:

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

ll T, n, m;
const int Max_m = 1e5 + 10;
const ll Max_t = 1e18 + 10;
struct node{
    ll a, b;
};
node q[Max_m];

bool check(){
    for (int i=1;i<m;++i){
        ll cur = q[i].b, nxt = q[i+1].b, ctime = q[i].a, ntime = q[i+1].a;
        if (max(cur,nxt)-min(cur,nxt) > ntime-ctime){
            return true;
        }
    }
    return false;
}

ll calNum(ll x){
    ll ans = 0;
    if (x <=0){
        return ans;
    }
    if (x&1){
        ans = (x+1)/2*x;
    }
    else{
        ans = x/2*(x+1);
    }
    return ans;
}

ll calMin(){
    ll ans = 0;
    for (int i=1;i<m;++i){
        ll cur = q[i].b, nxt = q[i+1].b, ctime = q[i].a, ntime = q[i+1].a;
        ll best = cur + nxt;
        if (cur > nxt){
        	swap(cur, nxt);
		}
        if (ntime-ctime == nxt-cur){
        	ans = ans + calNum(nxt) - calNum(cur-1);
		}
		else if (ntime - ctime >= best){
            ans = ans + calNum(cur) + calNum(nxt);
        }
        else{
        	ll des = ntime-ctime-(nxt-cur), tM = cur-des/2;
        	if (des & 1){
        	    ans = ans + calNum(cur)-calNum(tM-1) + calNum(nxt) - calNum(tM-1);
        	}
        	else{
            	ans = ans + calNum(cur)-calNum(tM-1) + calNum(nxt) - calNum(tM);
        	}
		}
		if (i<m-1){
			ans = ans - q[i+1].b;
		}
    }
    return ans;
}

ll calMax(){
    ll ans = 0;
    for (int i=1;i<m;++i){
        ll cur = q[i].b, nxt = q[i+1].b, ctime = q[i].a, ntime = q[i+1].a;
        if (cur > nxt){
            swap(cur, nxt);
        }
        if (ntime-ctime == nxt-cur){
        	ans = ans + calNum(nxt) - calNum(cur-1);
		}
		else{
        	ll des = ntime-ctime-(nxt-cur);ll tM = nxt+des/2;//����
        	if (des & 1){
        	    ans = ans + calNum(tM)-calNum(cur-1) + calNum(tM) - calNum(nxt-1);
        	}
        	else{
            	ans = ans + calNum(tM)-calNum(cur-1) + calNum(tM-1) - calNum(nxt-1);
        	}
		}
        if (i < m-1){
        	ans = ans - q[i+1].b;
        }
        if (ans > Max_t || ans<0){
        	ans = Max_t;
        	return ans;
		}
    }
    return ans;
}

void solve(){
    cin >> T >> n >> m;
    ll tmin = 0, tmax = 0;
    for (int i=1;i<=m;++i){
        cin >> q[i].a >> q[i].b;
    }
    if (check()){
        puts("No");
        return;
    }
    if(n==1)
    {
        if(q[1].b==T) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
        return;
    }
    if (q[1].a > 1){
    	ll qb0 = max(0LL, q[1].b-q[1].a+1), qb1 = q[1].b + q[1].a - 1;
    	tmin = tmin + calNum(q[1].b-1) - calNum(qb0-1);
    	tmax = tmax + calNum(qb1) - calNum(q[1].b);
	}//��ȥb[1]�Ŀ�ͷ����
    if (q[m].a < n){
    	ll qb0 = max(0LL,q[m].b-(n-q[m].a)), qb1 = q[m].b+(n-q[m].a);
    	tmin = tmin + calNum(q[m].b-1) - calNum(qb0-1);
    	tmax = tmax + calNum(qb1) - calNum(q[m].b);
	}//��ȥb[m]�Ŀ�ͷ����
    
    tmin += calMin();
    tmax += calMax();
    if (m == 1){
    	tmin += q[m].b;
    	tmax += q[m].b;
	}
//    cout <<"tmin: "<< tmin << " tmax: " << tmax << endl;
    if (T>=tmin && T<=tmax){
        puts("Yes");
    }
    else{
        puts("No");
    }
}

int main(void){
	int t;
//	cin >> t;
//	while (t--){
		solve();
//	}
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值