比赛链接:点击这里传送
(赛中已过)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,2n−now)。问最少需要多少天人头量能达到要求。
思路:
就第一天买1个人,然后让他每天晚上自己涨。涨到
k
−
1
+
n
o
w
>
=
n
k-1+now>=n
k−1+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
ai≥0,
a
i
−
a
i
+
1
≤
1
a_i-a_{i+1}≤1
ai−ai+1≤1,给出某些位置的数和一个数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;
}