◇题目传送门◆
给定一个数列由 1 1 1到 N N N排列,现第 i i i次操作擦掉第 i i i个数,如下:
求不能再操作后第 K K K个数。
多做几次操作就发现,第 K K K个数的值就是 2 K 2K 2K。
N N N似乎没有用处。。。
#include
#include
using namespace std;
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int T;
scanf("%d",&T);
while(T--) {
int n,x;
scanf("%d %d",&n,&x);
printf("%d\n",2*x);
}
return 0;
}
◇题目传送门◆
给定一个 N × M N\times M N×M的网格图,.
表示白色,*
表示黑色,求把其中一行和一列全部变成黑色的最少需改变的颜色数量。
考虑暴力。。。
每次找出最少需要改变的横行和竖列,
#include
#include
#include
#include
#include
using namespace std;
const int Maxn=5e4;
int N,M;
string s[Maxn+5];
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int T;
scanf("%d",&T);
while(T--) {
scanf("%d %d\n",&N,&M);
int r=0x3f3f3f3f,c=0x3f3f3f3f;
vector<int> R,C;
for(int i=0;i<N;i++) {
cin>>s[i];
int cnt=0;
for(int j=0;j<M;j++)
cnt+=(s[i][j]=='*');
if(M-cnt<r)
r=M-cnt,R.clear();
if(M-cnt==r)R.push_back(i);
}
int ans=0x3f3f3f3f;
for(int j=0;j<M;j++) {
int cnt=0;
for(int i=0;i<N;i++)
cnt+=(s[i][j]=='*');
if(N-cnt<c)
c=N-cnt,C.clear();
if(N-cnt==c)C.push_back(j);
}
for(int i=0;i<(int)R.size();i++)
for(int j=0;j<(int)C.size();j++)
ans=min(ans,r+c-(s[R[i]][C[j]]=='.'));
printf("%d\n",ans);
}
return 0;
}
◇题目传送门◆
给定三个串 s , t , p s,t,p s,t,p,每次操作可以从 p p p里任选一个字母并插入到 s s s中任意一个位置,问能否将 s s s完全转化为 t t t。
简要分析可以发现, s , p s,p s,p能够转化为 t t t的前提是 s s s是 t t t的一个子串,且 s , p s,p s,p中对应字母的数量和要大于等于 t t t中对应字母的数量。
所以我们就可以暴力判断。(因为 ∣ s ∣ , ∣ t ∣ , ∣ p ∣ |s|,|t|,|p| ∣s∣,∣t∣,∣p∣都小于 100 100 100)
#include
#include
#include
using namespace std;
char s[3][105];
int cnt[3][30];
int len[3];
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int T;
scanf("%d",&T);
while(T--) {
memset(cnt,0,sizeof cnt);
for(int i=0;i<3;i++) {
scanf("%s",s[i]);
len[i]=strlen(s[i]);
for(int j=0;j<len[i];j++)
cnt[i][s[i][j]-'a']++;
}
bool flag=true;
for(int i=0;i<26;i++)
if(cnt[0][i]+cnt[2][i]<cnt[1][i]) {
flag=false;
break;
}
if(flag==false) {
puts("NO");
continue;
}
int i=0,j=0;
while(i<len[0]&&j<len[1]) {
if(s[0][i]==s[1][j])
i++,j++;
else j++;
}
if(i!=len[0]) {
puts("NO");
continue;
}
puts("YES");
}
return 0;
}
◇题目传送门◆
给定一堆有 N N N个石子的石子堆,每次能从堆里取走 1 , 2 1,2 1,2或 K K K个石子,先取走所有石子的人获胜,求谁能够获胜。
考虑到 N , K N,K N,K范围极大,打SG函数显然要爆,所以我们考虑找一下规律:
当 K = 3 K=3 K=3时,写出对应的SG函数值:
N N N | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
S G ( N ) SG(N) SG(N) | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 | 1 |
似乎发现了一点规律:当 K = 3 , N ≡ 0 ( m o d    4 ) K=3,N\equiv0(\mod4) K=3,N≡0(mod4)时,后手胜利,否则先手胜利。
当 K ≠ 3 K\ne 3 K̸=3时,我们以 K = 4 K=4 K=4, K = 5 K=5 K=5和 K = 6 K=6 K=6为例,写出对应的 S G ( N ) SG(N) SG(N):
K = 4 : K=4: K=4:
N N N | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
S G ( N ) SG(N) SG(N) | 0 | 1 | 2 | 0 | 1 | 2 | 0 |
K = 5 : K=5: K=5:
N N N | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
S G ( N ) SG(N) SG(N) | 0 | 1 | 2 | 0 | 1 | 2 | 0 |
K = 6 : K=6: K=6:
N N N | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
S G ( N ) SG(N) SG(N) | 0 | 1 | 2 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 0 | 1 | 2 | 3 | 0 |
有点奇怪了。。。似乎没啥规律可言。
那再找两个吧。。。
K = 7 : K=7: K=7:
N N N | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
S G ( N ) SG(N) SG(N) | 0 | 1 | 2 | 0 | 1 | 2 | 0 |
K = 9 : K=9: K=9:
N N N | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
S G ( N ) SG(N) SG(N) | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 | 3 | 0 |
我们发现,当 K ̸ ≡ 0 ( m o d    3 ) K\not\equiv0(\mod3) K̸≡0(mod3)时,只要 N ≡ 0 ( m o d    3 ) N\equiv0(\mod3) N≡0(mod3),则后手必胜,反之先手必胜。
那当 K ̸ ≡ 0 ( m o d    3 ) K\not\equiv0(\mod3) K̸≡0(mod3)时,又有什么规律呢?
我们列出 K = 6 , K = 9 , K = 12 K=6,K=9,K=12 K=6,K=9,K=12时的两个后手必胜的状态的距离:
K = 6 : 3 , 4 , 3 , 4 , … K=6:3,4,3,4,\ldots K=6:3,4,3,4,…
K = 9 : 3 , 3 , 4 , 3 , 3 , 4 , … K=9:3,3,4,3,3,4,\ldots K=9:3,3,4,3,3,4,…
K = 12 : 3 , 3 , 3 , 4 , 3 , 3 , 3 , 4 , … K=12:3,3,3,4,3,3,3,4,\ldots K=12:3,3,3,4,3,3,3,4,…
似乎有点规律了,这些 S G SG SG函数值似乎是每 K + 1 K+1 K+1循环一次。所以我们就可以在常数时间内求得获胜的人。
至于为啥这样。。。暴力打表就是了
#include
#include
using namespace std;
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int T;
scanf("%d",&T);
while(T--) {
int N,K;
scanf("%d %d",&N,&K);
if(N==0) {
puts("Bob");
} else if(K==3) {
if(N%4==0)
puts("Bob");
else puts("Alice");
} else if(K%3!=0) {
if(N%3==0)
puts("Bob");
else puts("Alice");
} else if(K%3==0){
int t=K/3-1;
int s=3*t+4;
int tmp=N%s;
if(tmp%3==0&&tmp!=K)
puts("Bob");
else puts("Alice");
}
}
return 0;
}
◇题目传送门◆
给定 N N N条在坐标系的线段,每条线段的端点都是整点且都只有水平和垂直两种状态。线段不会重叠。问这些线段能够构成多少个矩形?
无脑数据结构题。。。
看题可发现,坐标是有负数 ,所以我们必须坐标平移。
对于两条水平的线段,若有 K K K条垂直的线段与它们相交,则这些线段对答案的贡献为 K ( K − 1 ) 2 \frac{K(K-1)}{2} 2K(K−1)。
那么我们可以枚举这两条水平的线段,并计算它们中间和它们相交的竖直的线段的数量 K K K。直接计算复杂度为 O ( N 3 ) O(N^3) O(N3)
考虑优化,我们利用树状数组维护水平线上所有位置上线段与第一条水平线相交的情况,那么在向上查询时,我们只需从树状数组上查询第二条线段区间中与它相交的情况,注意若高度不够时需要将它删除。
所以总时间复杂度为 O ( N 2 log 2 N ) O(N^2\log_2N) O(N2log2N)。
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int Maxn=5000;
const int O=5001;
struct BIT_1D {
int s[3*Maxn+5];
int siz;
inline int lowbit(int x) {return x&(-x);}
inline void init(int n) {
siz=n;
memset(s,0,sizeof s);
}
void add(int x,int val) {
while(x<=siz) {
s[x]+=val;
x+=lowbit(x);
}
}
int query(int x) {
int ret=0;
while(x>0) {
ret+=s[x];
x-=lowbit(x);
}
return ret;
}
int sum(int l,int r){return query(r)-query(l-1);}
};
struct Segment1 {
int x1,x2;
int y;
bool operator < (const Segment1 &rhs) const {return (y==rhs.y?x1<rhs.x1:y<rhs.y);}
};
struct Segment2 {
int y1,y2;
int x;
bool operator < (const Segment2 &rhs) const {return (y2==rhs.y2?x<rhs.x:y2<rhs.y2);}
};
int N;
vector<Segment1> s1;
vector<Segment2> s2;
BIT_1D t;
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d",&N);
for(int i=1;i<=N;i++) {
int x1,x2,y1,y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
if(x1==x2)s2.push_back(Segment2{min(y1,y2)+O,max(y1,y2)+O,x1+O});
else s1.push_back(Segment1{min(x1,x2)+O,max(x1,x2)+O,y1+O});
}
sort(s1.begin(),s1.end());
sort(s2.begin(),s2.end());
ll ans=0;
for(int i=0;i<(int)s1.size();i++) {
t.init(Maxn*2+3);
queue<int> q;
for(int j=0;j<(int)s2.size();j++)
if(s2[j].y2>=s1[i].y&&s2[j].y1<=s1[i].y&&s1[i].x1<=s2[j].x&&s2[j].x<=s1[i].x2) {
t.add(s2[j].x,1);
q.push(j);
}
for(int j=i+1;j<(int)s1.size()&&!q.empty();j++) {
while(!q.empty()&&s2[q.front()].y2<s1[j].y) {
t.add(s2[q.front()].x,-1);
q.pop();
}
int k=t.sum(s1[j].x1,s1[j].x2);
ans+=1LL*k*(k-1)/2;
}
}
printf("%lld\n",ans);
return 0;
}
E,F是什么神仙题???不会做。。。