E.Square
题意 :给定一个
x
x
x,要求找到一个
y
y
y 存在任意一个
k
k
k满足
x
=
⌊
y
1
0
k
⌋
x=\lfloor \frac{y}{10^{k}} \rfloor
x=⌊10ky⌋
思路:从
y
y
y去考虑感觉有点困难,那么我们从
x
x
x去找
y
y
y。考虑如果
x
x
x存在那么其一定为
y
∗
y
y*y
y∗y的前缀,所以可以对
x
x
x定义一个区间
l
i
=
x
∗
1
0
i
l_i=x*10^i
li=x∗10i
r
i
=
(
x
+
1
)
∗
1
0
i
−
1
r_i=(x+1)*10^i-1
ri=(x+1)∗10i−1
找到最后一个小于等于
r
r
r的数字,检查是否大于
l
l
l即可(判断是否再区间内)。
#include<bits/stdc++.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
using namespace std;
const int N=0;
const ll md=1e9+7;
const ll inf=1e18;
const ll eps=1e-9;
const double E=2.718281828;
ll x,y;
ll fd(ll key){
ll l=0,r=1e9;
while(l<r){
ll m=(l+r)/2+1;
if(m*m>key){
r=m-1;
}else{
l=m;
}
}
return l;
}
void solve(){
//fucking and strange
cin>>x;
ull l=x,r=x;//可能会爆
while(r<=inf){
ll y=fd(r);
// cout<<l<<" "<<r<<" "<<y<<" "<<y*y<<endl;
if(y*y>=l){
cout<<y<<endl;
return ;
}else{
l=l*10;
r=r*10+9;
}
}
cout<<-1<<endl;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
}
I.Link with Gomoku
题意:构造一个尺寸为n*m的棋盘,使其在五子棋的规则下达到平局。
思路:如果隔一个放不同的棋子,那么满足了行和列的要求,但是对于斜线是不满足的。所以我们隔两个不同去放棋子。最后要检查是否刚好满足先手和后手的棋子数量一样或者刚好多一个,否则去找到任意一个棋子变换即可。按照如上构造唯一不合法的情况是两者棋子数量差值为2。
#include<bits/stdc++.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
using namespace std;
const int N=1e3+5;
const ll md=1e9+7;
const ll inf=1e18;
const ll eps=1e-9;
const double E=2.718281828;
int jz[N][N],n,m;
void solve(){
//fucking and strange
cin>>n>>m;
int cnt[2]={0,0};
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(i%2){
if(j%4<2){
jz[i][j]=0;
}else{
jz[i][j]=1;
}
}else{
if(j%4<2){
jz[i][j]=1;
}else{
jz[i][j]=0;
}
}
cnt[jz[i][j]]++;
}
}
//cout<<cnt[0]<<" "<<cnt[1]<<endl;
if(cnt[0]-cnt[1]==2){
bool f=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(jz[i][j]==0){
f=1;
jz[i][j]=1;
break;
}
}
if(f){
break;
}
}
}else if(cnt[1]-cnt[0]==2){
bool f=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(jz[i][j]==1){
f=1;
jz[i][j]=0;
break;
}
}
if(f){ss'sssssss
break;
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(jz[i][j]){
cout<<"x";
}else{
cout<<"o";
}
}
cout<<endl;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
}
k.box
题意:给两个数组
a
,
b
a,b
a,b,有一些盒子,其中有些盒子上面有盖子,如果第
i
i
i个盒子被盖上盖子可以获得
a
i
a_i
ai分,有多个盖子只得一次分。
思路:想了很久的贪心,贪不出来,还是直接考虑DP吧。
根据题意可以知道,盖子分散开一定是最优的情况。
根据每个盒子上面是否有盖子,和有盖子的盒子的盖子转移方式我们可以设计出转移公式。
f
(
i
,
0
)
f(i,0)
f(i,0)表示没有盖子的情况 (本身没有盖子 或者 有盖子转移到前一个位置)
f
(
i
,
1
)
f(i,1)
f(i,1)表示有盖子的情况 (本身有盖子 或者 有盖子从前一个位置转移)
f
(
i
,
2
)
f(i,2)
f(i,2)表示本身有盖子且盖子向后转移
当
b
i
=
0
b_i=0
bi=0时,
没有转移出盖子(因为没有)且没有盖子移到对当前位置
f
(
i
,
0
)
=
m
a
x
(
f
(
i
−
1
,
0
)
,
f
(
i
−
1
,
1
)
)
f(i,0)=max(f(i-1,0),f(i-1,1))
f(i,0)=max(f(i−1,0),f(i−1,1))
因为没有盖子所以只能是由前一个位置向后转移一个盖子过来
f
(
i
,
1
)
=
f
(
i
−
1
,
2
)
f(i,1)=f(i-1,2)
f(i,1)=f(i−1,2)
因为没有盖子无法向后转移
f
(
i
,
2
)
=
0
f(i,2)=0
f(i,2)=0
当
b
i
=
1
b_i=1
bi=1时,
向前转移出盖子
f
(
i
,
0
)
=
f
(
i
−
1
,
0
)
+
a
i
−
1
f(i,0)=f(i-1,0)+a_{i-1}
f(i,0)=f(i−1,0)+ai−1
本身有盖子保持不变
f
(
i
,
1
)
=
m
a
x
(
f
(
i
−
1
,
0
)
,
f
(
i
−
1
,
1
)
)
+
a
i
f(i,1)=max(f(i-1,0),f(i-1,1))+a_{i}
f(i,1)=max(f(i−1,0),f(i−1,1))+ai
向后转移盖子
f
(
i
,
2
)
=
m
a
x
(
f
(
i
−
1
,
0
)
,
f
(
i
−
1
,
1
)
,
f
(
i
−
1
,
2
)
)
+
a
i
+
1
f(i,2)=max(f(i-1,0),f(i-1,1),f(i-1,2))+a_{i+1}
f(i,2)=max(f(i−1,0),f(i−1,1),f(i−1,2))+ai+1
最后答案取
m
a
x
(
f
(
n
,
0
)
,
f
(
n
,
1
)
,
f
(
n
,
2
)
)
max(f(n,0),f(n,1),f(n,2))
max(f(n,0),f(n,1),f(n,2))即可
#include<bits/stdc++.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
using namespace std;
const int N=1e6+5;
const ll md=1e9+7;
const ll inf=1e18;
const ll eps=1e-9;
const ll E=2.718281828;
int n,can[N],vis[N];
ll a[N],b[N],f[N][3];
void solve(){
//fucking and strange
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
}
for(int i=1;i<=n;i++){
if(b[i]==0){
f[i][0]=max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][2];
}else{
f[i][0]=f[i-1][0]+a[i-1];
f[i][1]=max(f[i-1][0],f[i-1][1])+a[i];
f[i][2]=max({f[i-1][0],f[i-1][1],f[i-1][2]})+a[i+1];
}
}
cout<<max({f[n][0],f[n][1],f[n][2]})<<endl;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t=1;
// cin>>t;
while(t--){
solve();
}
}
D.The Game of Eating
题意:有
n
n
n个人点菜有
m
m
m种菜品,由
1
1
1到
n
n
n循环的轮流点
k
k
k次,每种菜只能点一次。针对每个人对每种菜有一权值,问所有人在上述规则下满足对于自身的权值和最大的点的菜的集合是什么,升序输出,所有的菜。
思路:赛时就没有看到这题,被虚假的贪心折磨。直接从后往前考虑,如果自身当前的最大值的菜会被后面的人选择,那么我可以转而选择次大值的菜。
//It's better to have sex than to do questions
#include<bits/stdc++.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
using namespace std;
const int N=2005;
const ll md=1e9+7;
const ll inf=1e18;
const ll eps=1e-9;
const double E=2.718281828;
int n,m,k,vis[N];
vector<pair<int,int>>pp[N];
int pt[N];
void solve(){
cin>>n>>m>>k;
for(int i=1;i<=m;i++)vis[i]=0;
for(int i=0;i<n;i++){
pp[i].clear();
pt[i]=m-1;
for(int v,j=1;j<=m;j++){
cin>>v;
pp[i].push_back({v,j});
}
sort(pp[i].begin(),pp[i].end());
}
vector<int>ans;
int be=k%n-1<0?n-1:k%n-1;
// cout<<"be "<<be<<endl;
while(k--){
while(vis[pp[be][pt[be]].se]&&pt[be]>=0){
pt[be]--;
}
if(pt[be]>=0){
vis[pp[be][pt[be]].se]=1;
ans.push_back(pp[be][pt[be]].se);
}
be--;
if(be<0)be=n-1;
}
sort(ans.begin(),ans.end());
for(int v:ans){
cout<<v<<" ";
}
cout<<"\n";
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
}
H.0 and 1 in BIT
题意:给定长度为
n
n
n操作序列和
q
q
q次询问,只有A,B两种字母,代表两种操作。
A:对于给定的二进制串进行按位取反
B:对于给定的二进制串进行
+
1
+1
+1操作,舍去溢出的位数
一次询问给出 区间
[
l
,
r
]
[l,r]
[l,r]和二进制串
s
s
s
l
r
s
l \;\;\; r \;\;\; s
lrs
l
r
e
a
l
=
m
i
n
(
(
a
n
s
i
−
1
⊕
l
)
%
n
+
1
,
(
a
n
s
i
−
1
⊕
r
)
%
n
+
1
)
l_{real}=min((ans_{i-1} \oplus l )\%n+1, (ans_{i-1} \oplus r )\%n+1 )
lreal=min((ansi−1⊕l)%n+1,(ansi−1⊕r)%n+1)
r
r
e
a
l
=
m
a
x
(
(
a
n
s
i
−
1
⊕
l
)
%
n
+
1
,
(
a
n
s
i
−
1
⊕
r
)
%
n
+
1
)
r_{real}=max((ans_{i-1} \oplus l )\%n+1, (ans_{i-1} \oplus r )\%n+1 )
rreal=max((ansi−1⊕l)%n+1,(ansi−1⊕r)%n+1)
a
n
s
0
=
0
ans_0=0
ans0=0
对于每次询问输出经过区间操作后的二进制数,强制在线
思路:A为取反操作,B为
+
1
+1
+1然后舍去多余的位,可以看出这两个操作都在模
2
s
.
l
e
n
g
t
h
(
)
2^{s.length()}
2s.length()的意义下进行运算。
我们先随便取一段进行模拟,AAABB,设
s
s
s的十进制位
v
v
v,
m
o
d
=
2
s
.
l
e
n
g
t
h
(
)
mod=2^{s.length()}
mod=2s.length()
A的操作可以看作是
v
i
=
(
m
o
d
−
v
i
−
1
−
1
)
%
m
o
d
v_i=(mod-v_{i-1}-1)\%mod
vi=(mod−vi−1−1)%mod
B的操作可以看作是
v
i
=
(
v
i
−
1
+
1
)
%
m
o
d
v_i=(v_{i-1}+1)\% mod
vi=(vi−1+1)%mod
所以可以列出式子:
a
n
s
=
m
o
d
−
(
m
o
d
−
(
m
o
d
−
v
−
1
)
−
1
)
−
1
+
1
+
1
+
1
ans=mod-(mod-(mod-v-1)-1)-1+1+1+1
ans=mod−(mod−(mod−v−1)−1)−1+1+1+1
消去所有括号式子变成
a
n
s
=
m
o
d
−
m
o
d
+
m
o
d
−
v
−
1
+
1
−
1
+
1
+
1
+
1
=
m
o
d
−
v
+
1
+
1
ans=mod-mod+mod-v-1+1-1+1+1+1=mod-v+1+1
ans=mod−mod+mod−v−1+1−1+1+1+1=mod−v+1+1
两个式子结合起来可以发现
B对于答案的贡献 要么为
−
1
-1
−1,要么为
+
1
+1
+1,某一个B的贡献取决去在给定区间内其后面的A的个数的奇偶性。
A对于答案的贡献 要么为
v
v
v,要么为
−
v
−
1
-v-1
−v−1,取决与给定区间A的个数的奇偶性。
那么如何去快速统计在给定区间内 后面A的个数为奇数或者偶数的B的数量
维护A的后缀个数(sufa[]) 和 后面A的个数为奇数或者偶数的B的后缀个数(cntbo[],cntbe[])
设 后面A的个数为奇数的B的个数为
o
d
d
b
oddb
oddb 后面A的个数为偶数的B的个数为
e
v
e
n
b
evenb
evenb
在根据区间后面的A的个数的奇偶性质得到
o
d
d
b
oddb
oddb和
e
v
e
n
b
evenb
evenb
就可以计算得答案,注意取模和爆int,
//It's better to have sex than to do questions
#include<bits/stdc++.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
using namespace std;
const int N=2e5+5;
const ll md=1e9+7;
const ll inf=1e18;
const ll eps=1e-9;
const double E=2.718281828;
string s;
ll sufa[N],cntbo[N],cntbe[N];
ll n,q;
ll getv(string s){
ll res=0;
for(ll i=s.length()-1;i>=0;i--){
if(s[i]=='1'){
res+=(1ll<<(ll)(s.length()-1-i));
}
}
return res;
}
void pt(ll num,int sz){
string ans;
for(ll i=0;i<sz;i++){
if((num>>i)&1ll){
ans.push_back('1');
}else{
ans.push_back('0');
}
}
reverse(ans.begin(),ans.end());
cout<<ans<<endl;
}
void solve(){
cin>>n>>q;
cin>>s;
sufa[n+1]=0;
cntbo[n+1]=0;
cntbe[n+1]=0;
for(int i=n;i>=1;i--){
sufa[i]=sufa[i+1];
cntbo[i]=cntbo[i+1];
cntbe[i]=cntbe[i+1];
if(s[i-1]=='A'){
sufa[i]++;
}else{
if(sufa[i]%2){
cntbo[i]++;
}else{
cntbe[i]++;
}
}
}
ll l,r,ans=0;
while(q--){
cin>>l>>r>>s;
ll tl=l,tr=r;
l=min((ans^tl)%n+1ll,(ans^tr)%n+1ll);
r=max((ans^tl)%n+1ll,(ans^tr)%n+1ll);
ll sz=s.length();
ll num=getv(s),mod=1ll<<sz;
// cout<<"num "<<num<<" "<<mod<<endl;
ans=0;
ll init=0;
ll oddb,evenb;
if(!(sufa[r+1]%2)){
oddb=cntbo[l]-cntbo[r+1];
evenb=cntbe[l]-cntbe[r+1];
}else{
evenb=cntbo[l]-cntbo[r+1];
oddb=cntbe[l]-cntbe[r+1];
}
init=(sufa[l]-sufa[r+1])%2;
if(init){
ans=(ans+mod-(num+1)%mod)%mod;
}else{
ans=(ans+num%mod+mod)%mod;
}
// cout<<"l r "<<l<<" "<<r<<endl;
// cout<<sufa[l]-sufa[r+1]<<" | "<<ans<<" - "<<oddb<<" + "<<evenb<<endl;
ans=((ans+evenb%mod)%mod+mod-oddb%mod)%mod;
pt(ans,sz);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t=1;
// cin>>t;
while(t--){
solve();
}
}
G.Link with Centrally Symmetric Strings
题意:给定一个字符串判断其是否可以用几个中心对称子串连接而成。
一个子串为中心对称子串,当且仅当它满足以下条件之一:
1.字符串
S
S
S为空字符串
2.
S
=
o
∣
s
∣
x
∣
z
S=o|s|x|z
S=o∣s∣x∣z,
s
s
s是四个字母之一:o,s,x,z
3.
S
=
b
S
q
∣
d
S
p
∣
p
S
d
∣
n
S
u
∣
u
S
n
∣
o
S
o
∣
s
S
s
∣
x
S
x
∣
z
S
z
S=bSq|dSp|pSd|nSu|uSn|oSo|sSs|xSx|zSz
S=bSq∣dSp∣pSd∣nSu∣uSn∣oSo∣sSs∣xSx∣zSz,即,
S
S
S 以一对中心对称的字母开始和结束,并且中间部分也是中心对称的子串。
思路:(打了EDU睡不着了,补一补),按照马拉车判回文的方法取出每一个位置的满足题目要求的最长半径。维护一个可以拼接到的位置,如果在倒数第一第二个则为YES,反之为NO
//It's better to have sex than to do questions
#include<bits/stdc++.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
using namespace std;
const int N=2e6+5;
const ll md=1e9+7;
const ll inf=1e18;
const ll eps=1e-9;
const double E=2.718281828;
map<char,char>mp;
string s,t;
void init(){
mp['o']='o';
mp['s']='s';
mp['x']='x';
mp['z']='z';
mp['b']='q';
mp['d']='p';
mp['q']='b';
mp['p']='d';
mp['n']='u';
mp['u']='n';
mp['#']='#';
}
bool check(char x,char y){
if(mp[x]==y){
return 1;
}else{
return 0;
}
}
int p[N],f[N],lst[N];
void solve(){
cin>>s;
t.clear();
t.push_back('%');
t.push_back('#');
for(int i=0;i<s.length();i++){
if(!mp.count(s[i])){
cout<<"NO"<<"\n";
return ;
}else{
t.push_back(s[i]);
t.push_back('#');
}
}
int rigmst=0,c=0,now=1;
for(int i=1;i<t.size();i++){
if(t[i]!='s'&&t[i]!='o'&&t[i]!='x'&&t[i]!='z'&&t[i]!='#'){
p[i]=0;
continue;
}
if(i<rigmst){
p[i]=min(p[c-(i-c)],rigmst-i+1);
}else{
p[i]=1;
}
while(i-p[i]>=0&&i+p[i]<t.size()&&check(t[i+p[i]],t[i-p[i]])){
p[i]++;
}
if(i+p[i]-1>rigmst){
rigmst=i+p[i]-1;
c=i;
}
if(t[i]=='#'&&p[i]==1)p[i]=0;
if(i-p[i]<=now&&p[i]>0){
now=max(now,i+i-now-1);
}
}
if(now>=t.size()-2){
cout<<"YES"<<"\n";
}else{
cout<<"NO"<<"\n";
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t=1;
init();
cin>>t;
while(t--){
solve();
}
}