A | B | C | D | E | F | G |
---|---|---|---|---|---|---|
√ | √ | √ | √ | √ | ○ |
( √:做出; ●:尝试未做出; ○:已补题 )
题目地址:https://codeforces.com/contest/1373
题意:
思路:
代码:
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
int main()
{
//freopen("input.txt","r",stdin);
int T=read();
while(T--)
{
LL a=read(),b=read(),c=read();
printf("%d ",a<c?1:-1);
printf("%d\n",c<a*b?b:-1);
}
return 0;
}
题意:
思路:
代码:
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
int main()
{
//freopen("input.txt","r",stdin);
int T=read();
char s[105];
while(T--)
{
scanf("%s",s);
int n0=0,n1=0;
for(int i=0;s[i];i++) n0+=s[i]=='0',n1+=s[i]=='1';
int x=min(n0,n1);
puts(x&1?"DA":"NET");
}
return 0;
}
题意:(经过简化后的题意)给出一个 ‘+’ ‘-’ 序列,从前往后遍历,,每遇到 + 就 +1,- 就 -1。初始值从 0 到 inf 遍历,然后对于每个初始值遍历一遍整个序列,如果到了某一次之后这个值变成了负数,就 conintue(初始值++)。输出总共访问这个序列的次数。
思路:先求一个序列的前缀和 s,显然我们对于每个初始值 x,要找的是最先的 s[i]<-x 的位置,如果没有这样的位置那么就结束了(要注意最后一次实际上访问了整个序列,而之前的访问了那个位置之前的序列)。所以一开始遍历 s 计算数组 fi,其意义为 fi[i] 表示 s[d]<-i 成立的 d 的最小值,然后后面遍历初始值求和就可以了。
代码:
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=1e6+5;
int s[maxn],fi[maxn],n;
char t[maxn];
int main()
{
//freopen("input.txt","r",stdin);
int T=read();
while(T--)
{
scanf("%s",t+1);
n=strlen(t+1);
REP(i,0,n+3) s[i]=fi[i]=0;
REP(i,1,n) s[i]=t[i]=='+'?1:-1;
REP(i,1,n) s[i]+=s[i-1];
REP(i,1,n) if(s[i]<0 && !fi[-s[i]-1]) fi[-s[i]-1]=i;
LL ans=0;
REP(i,0,n)
if(fi[i]) ans+=fi[i];
else break;
printf("%lld\n",ans+n);
}
return 0;
}
题意:一个下标从 0 开始的数组,可以进行一次操作,翻转其中任意一个子串(连续的),使得这个数组偶下标的和最大(a[0]+a[2]+a[4]+…),求出这个最大值。
思路:进行几次试验就能发现,如果翻转长度为奇数,那么不会有任何影响;如果翻转长度为偶数,那么对于某个翻转区间,相当于对于每个偶下标的元素,用它们后面或者前面的元素去替换它们,比如说对于 a0,a1,a2,a3,a4,a5,a5
,我们可以选择一段连续的偶下标区间,比如说 a2,a4
,然后用它们前面后者后面的元素去替换它们(也就是可以用 a1,a3
或者 a3,a5
去替换),那么问题就变为:分别处理用前面替换和后面替换的,对于其中一种,把所有替换方案所带来的增益(可能是负数)按照原来顺序存起来,然后找出一个最大的连续和(可以计算前缀和然后算最大的差),用这个和去加在原来答案上就行了。
代码:
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=2e5+5;
int n,a[maxn];
LL b[maxn];
LL cal(VI x)
{
if(!x.size()) return 0;
int m=x.size();
b[0]=x[0];
REP(i,1,m-1) b[i]=b[i-1]+x[i];
LL ret=0,minx=0;
REP(i,0,m-1)
{
ret=max(ret,b[i]-minx);
minx=min(minx,b[i]);
}
return ret;
}
int main()
{
//freopen("input.txt","r",stdin);
int T=read();
while(T--)
{
n=read();
REP(i,0,n-1) a[i]=read();
LL ans=0,q=0;
REP(i,0,n-1) if(!(i&1)) ans+=a[i];
// left
VI f;
for(int i=2;i<n;i+=2) f.pb(a[i-1]-a[i]);
q=max(q,cal(f));
// right
f.clear();
for(int i=0;i+1<n;i+=2) f.pb(a[i+1]-a[i]);
q=max(q,cal(f));
printf("%lld\n",ans+q);
}
return 0;
}
题意:设 f(x) 表示 x 的数位和,给出 n 和 k( 1 ≤ n ≤ 150 , 0 ≤ k ≤ 9 1\le n\le150, \ 0\le k\le 9 1≤n≤150, 0≤k≤9),找出最小的 x x x ,使得 f ( x ) + f ( x + 1 ) + . . . + f ( x + k ) = n f(x)+f(x+1)+...+f(x+k)=n f(x)+f(x+1)+...+f(x+k)=n 。
思路:乍一看好像这个数很大,但是如果考虑 k ≥ 2 k\geq 2 k≥2 的情况,可以发现,平均下来每个数数位和最多是 50,我就草率的认为这个数最大就是 3000000,然后就可以一开始预处理出所有 k ≥ 2 k\geq 2 k≥2 的答案。
然后对于 k = 0 k=0 k=0 的情况,显然要尽可能多地取 9,然后第一位取剩下的余数;
对于 k = 1 k=1 k=1 的情况,有点复杂:如果 n 为奇数,那么最优的情况一定是 xxxx8,xxxx9
,其中第一位取最后的余数,然后其它的都是 9;如果 n 为偶数,经过周密思考我发现,只有可能是 xxxx9,xxxx0
这种情况,再进一步,最优的情况一定是 xxx89,xxx90
,然后第一位取最后的余数,其它的都是 9 。
比赛的时候这一题搞了好久,写得也很乱。
代码:
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
LL solve0(int n)
{
int n9=0;
while(n>=9) n-=9,n9++;
LL ret=0;
if(n) ret=n;
while(n9--) ret=ret*10+9;
return ret;
}
LL solve1(int n)
{
if(n%2==0)
{
if(n<10) return -1;
n-=9;
LL ret=0;
if(n<=17)
{
ret+=n/2;
ret=ret*10+9;
return ret;
}
n-=17;
int n9=n/18;
n-=n9*18;
ret=n/2;
while(n9--) ret=ret*10+9;
ret=ret*10+8;
ret=ret*10+9;
return ret;
}
int n9=n/18;
if(!n9) return n/2;
else
{
n-=n9*18;
int x=n/2+1;
LL ret=x;
n9--;
while(n9--) ret=ret*10+9;
ret=ret*10+8;
return ret;
}
}
int F(LL x)
{
int ret=0;
while(x) ret+=x%10,x/=10;
return ret;
}
int N=3000000;
LL f[10000020];
int ans[155][10];
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
REP(i,1,N+9) f[i]=F(i)+f[i-1];
REP(k,0,9) REP(i,0,150) ans[i][k]=-1;
REP(k,2,9) REP(i,k,N+9)
{
int q=i==k?f[i]-f[0]:f[i]-f[i-k-1];
if(q<=150 && ans[q][k]<0) ans[q][k]=i-k;
}
//REP(i,1,150) REP(k,2,9) cout<
int T=read();
while(T--)
{
int n=read(),k=read();
if(k==0) printf("%lld\n",solve0(n));
else if(k==1) printf("%lld\n",solve1(n));
else printf("%d\n",ans[n][k]);
}
return 0;
}
题意:n 个需求点围成一个圆,每个点需要 a[i] ,每一组相邻的需求点之间有一个供应点,可以提供 b[i]。供应点只能给相邻的点提供供应(且过程中不能为负数,个人理解),问是否存在供应方案。
思路:
(比赛时的思路,不过没写完)首先如果有一个需求点大于两边的供应点之和,那么肯定不存在;其次如果有一个供应点大于等于两边的需求点之和,那么可以从这里打开这条链,然后遍历一次就可以判断是否存在;所以问题就变为对于剩下情况的求解:假设 1 号供应点给 1 号需求点的值为 x,那么可以根据这个值计算出后面所有供应的值,然后就有好多不等式,然后看 x 能否取到可行解就行了(不知道这个思路对不对)。
(以上思路不对,补题后的思路)事实上存在一个单调性,假设 b1 给 a1 的值为 x,那么通过贪心的往后推,可以最终推出 bn 给 a1 的值 y,并且在可行条件下(也就是中间不存在不可行的分配),x 越大,y越小,而且在 y ≥ 0 y\ge 0 y≥0 的条件下,x 越大越好,因为 x 每加一,y 最多减一,所以 x 越大越好。所以我们的目标就是通过二分搜索,找到在 y ≥ 0 y\ge 0 y≥0 的条件下最大的 x,然后判断是否满足 a1 就行了。
(另外一个思路)先补一个赫尔婚姻定理:
设二分图 G={X+Y, M} ,使得 X 完全匹配的充要条件是 ∀ M ⊆ X , ∣ M ∣ ≤ N G ( x ) \forall M\subseteq X, \ |M|\le N_G(x) ∀M⊆X, ∣M∣≤NG(x) 。也就是说对于 X 中任意的点集,X 的大小一定不大于与这个点集相连的所有结点的数目(否则相当于很多人抢很少的东西)。这个必要性很好看出来,充分性就不说了(其实是不会)。
那么对于这道题,其实可以看成一个二分图,要求对于任意个 a,与其相连的 b 的和要大于等于这些 a 的和,那么如何优雅地表示任意个 a 呢?其实只需要保证任意连续多个 a 成立就行了,因为连续一定比不连续更优(不连续说明 b 更多)。那么问题转化为:设 A 是 a 的前缀和,B 是 b 的前缀和,如果存在 A[r] - A[l] > B[r] - B[l-1] 就说明不可行;于是就可以预处理出 A[r]-B[r] 和 A[l]-B[l-1] 这两个数组,然后遍历一遍就可以了。还要注意因为是一个圈,所以要加倍展开;以及要特判全部的 a 和全部的 b 的比较,因为以上计算时 b 都要比 a 多一个。
代码:
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=1e6+5;
LL a[maxn],b[maxn];
int n;
LL get(LL x)
{
LL xx=x;
REP(i,2,n)
{
LL y=b[i-1]-x;
LL z=y>=a[i]?0:a[i]-y;
if(z>b[i]) return -1;
x=z;
}
return b[n]-x;
}
int main()
{
//freopen("input.txt","r",stdin);
int T=read();
while(T--)
{
n=read();
REP(i,1,n) a[i]=read();
REP(i,1,n) b[i]=read();
LL l=0,r=b[1]+1,mid;
while(l<r-1)
{
mid=(l+r)>>1;
if(get(mid)>=0) l=mid;
else r=mid;
}
puts(l+get(l)>=a[1]?"YES":"NO");
}
return 0;
}
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=3e6+5;
LL a[maxn],b[maxn],A[maxn],B[maxn];
int main()
{
//freopen("input.txt","r",stdin);
int T=read();
while(T--)
{
int n=read(); int nn=n;
REP(i,3,n+2) a[i]=read();
REP(i,3,n+2) b[i]=read();
a[1]=a[n+1]; a[2]=a[n+2];
b[1]=b[n+1]; b[2]=b[n+2];
REP(i,n+3,2*n+2) a[i]=a[i-n],b[i]=b[i-n];
n=2*n+2;
REP(i,1,n) a[i]+=a[i-1],b[i]+=b[i-1];
REP(i,1,n) A[i]=a[i]-b[i],B[i]=a[i]-b[i-1];
if(a[n]-a[n-nn]>b[n]-b[n-nn]) {puts("NO"); continue;}
int flag=1;
LL minx=1e18;
REP(i,1,n)
{
if(A[i]>minx) {flag=0; break;}
minx=min(minx,B[i]);
}
puts(flag?"YES":"NO");
}
return 0;
}
题意:
思路:
代码: