题目
A. Polycarp and Sums of Subsequences
题意:t组样例,每组样例输入从小到大输入7个数构成b数组,问b数组中是否存在三个数,通过给出的方法可以得到b数组。
思路:t的范围5000,同时每组样例才7个数,我们直接暴力枚举每次选的那三个元素就行,最后去跟b数组比较看是否相同。
#include
using namespace std;
int a[10],b[10];
bool cek(){
sort(b+1,b+8);
for(int i=1;i<=7;i++){
if(a[i]==b[i]) continue;
return false;
}
return true;
}
void solve(){
for(int i=1;i<=7;i++) scanf("%d",&a[i]);
for(int i=1;i<=7;i++){
for(int j=i+1;j<=7;j++){
for(int k=j+1;k<=7;k++){
int cnt=0;
b[++cnt]=a[i];
b[++cnt]=a[j];
b[++cnt]=a[k];
b[++cnt]=a[i]+a[j];
b[++cnt]=a[i]+a[k];
b[++cnt]=a[j]+a[k];
b[++cnt]=a[i]+a[j]+a[k];
if(cek()){printf("%d %d %d\n",a[i],a[j],a[k]);return;}
}
}
}
}
int main(){
int t;scanf("%d",&t);
while(t--) solve();
}
B. Missing Bigram
题意:一个单词的二元组是其中两个相邻字母的序列,给了 n − 2 n-2 n−2个二元组,要求你构造一个长度为n的只包含 ′ a ′ 'a' ′a′和’ ′ b ′ 'b' ′b′的字符串s,s的所有二元组中随便删除一个,使得剩下的二元组等于给出的 n − 2 n-2 n−2个二元组
思路:
思路:直接以第一个二元组为前缀,之后在每次读入二元组的时候,判断一下当前的字符串s的末尾是否为读入二元组的第一个字符,不同则加入第一个字符,flag标记一下(这个就是删掉的那个)。随后可以就直接“拼接”起来(加上二元组的第二个字符);最后flag要是没有标记,就随便加一个 ′ a ′ 'a' ′a′或 ’ b ′ ’b' ’b′就行了
#include
using namespace std;
int n;
void solve(){
string ans;
bool flag=false;
cin>>n>>ans;
for(int i=2;i<=n-2;i++){
string ss; cin>>ss;
if(ans[ans.length()-1]!=ss[0]){
ans.push_back(ss[0]);
flag=true;
}
ans.push_back(ss[1]);
}
if(!flag) ans+="a";
cout<<ans<<"\n";
}
int main(){
int t;scanf("%d",&t);
while(t--) solve();
}
C. Paint the Array
题意:t组样例。每个样例给一个包含n个元素的序列,问是否存在一个数d,满足相邻的元素为满足下列两种形式之一,可以输出d,不可以输出0
形式1: (a1%d==0,a2%d!=0,a3%d==0,a4%d!=0....a(2x+1)%d==0,a(2x+2)%d!=0)
即:
if(i%2==1) ai%d==0;
else if(i%2==0) ai%d!=0;
形式2: (a1%d!=0,a2%d==0,a3%d!=0,a4%d==0....a(2x+1)%d!=0,a(2x+2)%d==0)
即:
if(i%2==1) ai%d!=0;
else if(i%2==0) ai%d==0;
思路:
开始想了一下质因数分解,感觉div3C题不至于这么搞,看了眼数据范围,发现果然不太行
发现对于第i个位置的元素,需要跟直接相邻的元素要没得关系,但跟位置i%2相同的所有元素有关,于是想到了对位置奇偶,然后分别求出奇偶位置所有元素的gcd值,对于得到的两个gcd值去暴力判断一下是否符合条件就行了,都不满足就输出0。
求gcd是因为这样可以保证所有i%2相同的元素去%gcd是等于0,即其中一种状态。
#include
using namespace std;
typedef long long ll;
const ll N=105;
ll n,a[N],b[N];
bool cek(ll d){
for(ll i=1;i<=n;i++){
if(a[i]%d==0) b[i]=-1;
else b[i]=1;
}
for(ll i=2;i<=n;i++){
if(b[i]!=b[i-1]) continue;
return false;
}
return true;
}
void solve(){
scanf("%lld",&n);
for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
ll gcd1=a[1],gcd2=a[2];
for(ll i=3;i<=n;i++){
if(i%2==1) gcd1=__gcd(gcd1,a[i]);
else gcd2=__gcd(gcd2,a[i]);
}
if(cek(gcd1)) printf("%lld\n",gcd1);
else if(cek(gcd2)) printf("%lld\n",gcd2);
else puts("0");
}
int main(){
int t;scanf("%d",&t);
while(t--) solve();
}
D. Array and Operations
题意:t组样例,每组样例第一行读入两个数字n、k,代表给你一个包含n个元素的序列,要进行k次删除操作,每次操作删除两个数 a i , a j ( i ! = j ) ai,aj(i!=j) ai,aj(i!=j),同时会得到一个分数 s c o r e = m i n ( a i / a j , a j / a i ) score=min(ai/aj,aj/ai) score=min(ai/aj,aj/ai),总分sum为剩下的 ( n − 2 k ) (n-2k) (n−2k)个元素和加上每次删除操作得到的分数。
思路:贪心,将a数组从小到大排序后,规定剩下来的一定是最小的那 ( n − 2 k ) (n-2k) (n−2k)个数,删除的是大的那 2 k 2k 2k个数,删除策略为每次删除中间间隔 k − 1 k-1 k−1个元素的两个元素,即分子为 a [ i ] a[i] a[i],分母为 a [ i + k ] a[i+k] a[i+k],这样计算出来的总分最小。(口胡的)
#include
using namespace std;
const int N=105;
int n,k,a[N],sum;
void solve(){
sum=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
for(int i=1;i<=n-2*k;i++) sum+=a[i];
for(int i=n-2*k+1,ci=1;ci<=k;ci++,i++) sum+=(a[i]/a[i+k]);
printf("%d\n",sum);
}
int main(){
int t;scanf("%d",&t);
while(t--) solve();
}
题意:t组样例,每组样例给定一个包含n个元素的b数组,问是否可以构造出一个长度为n的a数组,满足a数组(环)中每个元素在到达 i i i位置时的元素和等于 b [ i ] b[i] b[i]。
对于元素 a [ i ] a[i] a[i]
到达位置j | 对位置j的贡献 |
---|---|
j=i | a[i] |
j=i+1 | 2*a[i] |
j=i+2 | 3*a[i] |
… | … |
j=(i+k)%n+1 | (k+2)⋅a[i] |
j=(i+n−2)%n+1 | n⋅a[i] |
思路:这个肯定b数组的 s u m sum sum要是 ( 1 + n ) ∗ n / 2 (1+n)*n/2 (1+n)∗n/2的倍数,因为要求 1 ≤ a [ i ] ≤ 1 0 9 1≤a[i]≤10^9 1≤a[i]≤109,所以在任何情况在都应该会满足 s u m % ( ( 1 + n ) ∗ n / 2 ) = = 0 sum\%((1+n)*n/2)==0 sum%((1+n)∗n/2)==0。同时我们会发现这是一个n元一次方程,剩下的就是解方程组就行了(本题高斯消元肯定不行)
/*
n=3, b[1] b[2] b[3]
b[1]=a[1]+3*a[2]+2*a[3] ①式
b[2]=2*a[1]+a[2]+3*a[3] ②式
由①-②得:
b[1]-b[2]=-a[1]+2*a[2]-a[3] ③式
③式等价变换:
2*a[2]=b[1]-b[2]+a[1]+a[3] ④式
④式左右两边同时加a[2]
3*a[2]=b[1]-b[2]+(a[1]+a[2]+a[3]) ⑤式
(di=a[1]+a[2]+a[3])
⑤式等价变换:
n*a[2]=b[1]-b[2]+di ⑥式
⑥式等价变换:
a[2]=(b[1]-b[2]+di)/n
(a[i]=(b[(i-1+n)%n]-b[i]+di))/n
*/
#include
using namespace std;
typedef long long ll;
const ll N=4e4+5;
ll n,b[N],t[N],sum;
void solve(){
sum=0;
scanf("%lld",&n);
for(ll i=0;i<n;i++){
scanf("%lld",&b[i]);
sum+=b[i];
}
ll di=(1+n)*n/2;
if(sum%di!=0) {puts("NO");return;}
sum/=di;
for(ll i=0;i<n;i++){
ll nu=sum+b[(i-1+n)%n]-b[i];
if(nu<=0||nu%n!=0){puts("NO");return;}
t[i]=nu/n;
}
puts("YES");
for(ll i=0;i<n;i++) printf("%lld%c",t[i],(i==n-1)?'\n':' ');
return;
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
}
F. Reverse
题意:问x经过变换是否可以等于y。
变换方式:在x的二进制形式下,末尾可以加上0或1,随后反转二进制数,删除前导零,最后再将新的二进制数转成十进制数就是新数了。
思路:就一样例,x和y的大小也才 1 e 18 1e18 1e18,直接模拟变换,对x进行bfs,判断是否存在等于y的数,为了避免TLE,因为y的二进制数最多才64位,我们可以在bfs的时候剪枝,将那些二进制数形式下长度大于64的给跳过。保险起见,我是当长度大于 l e n b + 64 lenb+64 lenb+64时跳过,其中 l e n b lenb lenb为y在二进制形式下的长度。
#include
using namespace std;
typedef long long ll;
ll x,y,lenb;
bool flag;
string Y;
set<string>st1;
string change(ll x){
string s="",ans="";
while(x){
ll tt=x%2;
x/=2;
s+=(tt+'0');
}
ll idx=0;
for(ll i=s.length()-1;i>=0;i--){
if(s[i]=='0') continue;
idx=i;break;
}
for(ll i=idx;i>=0;i--) ans+=s[i];
return ans;
}
string change1(string s,int x){
s+=(x+'0');
string ans="";
ll idx=0;
for(ll i=s.length()-1;i>=0;i--){
if(s[i]=='0') continue;
idx=i;break;
}
for(ll i=idx;i>=0;i--) ans+=s[i];
return ans;
}
void bfs(ll x){
queue<string>q;
map<string,bool>mp;
q.push(change(x));
while(!q.empty()){
auto u=q.front(); q.pop();
if(mp.count(u)) continue;
if(u.length()>lenb+64) continue;
if(u==Y) {flag=true;return;}
st1.insert(u);
mp[u]=true;
for(int i=0;i<2;i++){
string ss=change1(u,i);
if(!mp.count(ss)) q.push(ss);
}
}
}
int main(){
scanf("%lld%lld",&x,&y);
Y=change(y);
lenb=Y.length();
bfs(x);
if(flag) puts("YES");
else puts("NO");
return 0;
}
G. Trader Problem
有时间再补