水一篇题解,不多解释
可以看到,n只有1e5,m只有100,直接暴力就可以了,开一个1e5大小的桶标记所有数字,如果这个数字是ai的倍数,标记被淘汰,执行m次。其他方法,大概可以试试把m个数字素因子分解,然后用类似线性筛的方法预处理,但是估计跑的更慢···
#include
#include
using namespace std;
const int N=1e5+1;
int a[N]={0};
int main(){
int n,m; scanf("%d%d",&n,&m);
while(m--){
int x; scanf("%d",&x);
for(int i=x;i<=n;i+=x){
a[i]=1;
}
}
int cnt=0;
for(int i=1;i<=n;i++)
if(!a[i])
cnt++;
printf("%d\n",cnt);
return 0;
}
倒着枚举就好了,因为每次操作都只对当前位置前面的开关起作用,相对的,最靠后的按钮就是必须按下去的按钮,依次类推,从后往前按下所有按钮即可。但是我们不能每次真的把0变成1,1变成0,复杂度太高了,所以我们用一个cnt做标记。如果cnt=0,就正常1是1,0是0;如果cnt=1,说明我们有操作,那就把1看成0,0看成1.整个过程再用异或运算优化一下,这个优化最好自己手推几个数据试一下,或者debug一下就不难理解。
#include
#include
using namespace std;
const int N=2e5+1;
char x[N];
int main(){
int len; scanf("%d",&len);
scanf("%s",x);
int cnt=0,cur=0;
for(int i=len-1;i>=0;i--){
x[i]-='0';
if(x[i]^cur){
cnt++;
cur^=1;
}
}
printf("%d\n",cnt);
return 0;
}
其实原本不想写这题的代码·····
说下思路先,很明显我们要用给出的串打乱顺序凑出T的子串,反过来想,我们只要枚举T的子串,看看这个子串能不能被凑出来就可以了。这样既好写又方便去重。
写法1就是直接取出所有的子串,然后放入set去重,或者放入map然后自己手动判断一下是否这个子串和之前的子串重复。然后判断子串的字母数量,如果子串能被给出的串凑出来,那么字母数量应该是<=的关系。
然后是怎么取子串。首先,子串长度肯定和给出的串相同,记为len,考虑顺次取出每一个长度len的子串,相当于在前一个子串的基础上去掉第一个字母,然后在尾部补一个字母,这样就能O(1)完成操作,但是我这里直接string的substr操作,懒得写。
然后,兴致勃勃地交上代码,90分,MLE,好,换写法。
考虑我们要完成的是字符串的存取和判重,直接用hash即可,然后因为是所有长度为len的子串依次取出,考虑滚动hash,即按照我们前面提到的这个子串和上一个子串的关系,既然上一个子串的hash值已知,那么,我只需要去掉第一个字母的权重,左移一位,然后再补上一个新字母的权重即可,这就是滚动hash。上代码(本人hash垃圾,不会选base和mod,WA了好多发,最后hash跑两个才AC,提醒大家mod要大一点)
#include
#include
#include
using namespace std;
typedef long long LL;
const int N=2e5+1;
const int mod1=1e7+3;
map<pair<LL,LL >,bool>q;
const int base1=2;
const int base2=3;
//const int base3=5;
int num[27]={0},cur[27]={0};
LL vhash1=0,vhash2=0,vhash3=0;
bool q1[mod1+5],q2[mod1+5];
LL quickpow(LL x,LL k,LL mod){
LL res=1;
while(k){
if(k&1){
res=res*x%mod;
}
x*=x; x%=mod;
k>>=1;
}
return res;
}
int main(){
string x,y; cin>>x>>y;
int len1=x.size(),len2=y.size();
for(int i=0;i<len1;i++){
num[x[i]-'a']++;
cur[y[i]-'a']++;
vhash1*=base1;
vhash1+=y[i];
vhash1%=mod1;
vhash2*=base2;
vhash2+=y[i];
vhash2%=mod1;
}
q1[vhash1]=1;
q2[vhash2]=1;
//cout<<"hash"<
int res=0;
for(int i=0;i<26;i++){
if(cur[i]>num[i])
break;
if(i==25){
res++;
//cout<<0<
}
}
int t1=quickpow(base1,len1-1,mod1);
int t2=quickpow(base2,len1-1,mod1);
for(int i=1;i<=len2-len1;i++){
vhash1-=(y[i-1])*t1%mod1;
vhash1*=base1;
vhash1+=y[i+len1-1];
vhash1=(vhash1%mod1+mod1)%mod1;
//cout<<"hash"<
vhash2-=(y[i-1])*t2%mod1;
vhash2*=base2;
vhash2+=y[i+len1-1];
vhash2=(vhash2%mod1+mod1)%mod1;
cur[y[i-1]-'a']--;
cur[y[i+len1-1]-'a']++;
if(q1[vhash1]&&q2[vhash2]) continue;
q1[vhash1]=1;
q2[vhash2]=1;
for(int j=0;j<26;j++){
if(cur[j]>num[j])
break;
if(j==25){
res++;
//cout<
}
}
}
cout<<res<<endl;
return 0;
}
挺坑人的一个题目,思维题,当三个灯亮的时候肯定是位于中间的楼层,两个灯亮的时候要特判,因为如果一共只有两层,两个灯亮就不知道在第一层还是第二层。
#include
#include
#include
using namespace std;
int main()
{
int T; scanf("%d",&T);
while(T--){
int n,m; scanf("%d%d",&n,&m);
int a[4];
for(int i=0;i<m;i++){
scanf("%d",a+i);
}
if(n==1){
printf("1\n");
continue;
}
else if(n==2){
printf("-1\n");
continue;
}
if(m==3)
printf("%d\n",a[1]);
else if(m==2){
if(a[1]==2)
printf("1\n");
else
printf("%d\n",n);
}
}
return 0;
}
这题贪心就好,尽可能的把一样的字符放在前面,只要数量够就放,那么数量就=字符数量/要凑成的字符串数量,(向下取整,因为可能不是整除),然后26个字母都做一遍,求和。可能爆int.
#include
#include
#include
using namespace std;
int main()
{
int n; scanf("%d",&n);
long long s=0;
for(int i=0;i<26;i++){
int x; scanf("%d",&x);
s+=x/n;
}
printf("%lld\n",s);
return 0;
}
这题,还是早早联想画图吧,简单想法就是如果两个数,知道了其中一个,又知道了它们的和,就可以求出两个数字。这也是出题人的意思,关键在于,如果求出了这两个数字x,y,如果在这之前,a也和x相连,但是a和x都为止,现在x已知,那么a也能求出来。
也就是要能连带的求出数字。
也就是,x求出来之后,能把跟x有关系的数字都找到并求出来。这不就是个连通块么,并查集维护即可。然而我··没写并查集,准确的说,我准备先交个爆搜练练手,结果··它·· 过了··,这数据是真的··有点水了··也可能我剪枝之后不太好卡···
最重要的一点是,就是我爆搜,也是做不出来环的情况的··,数据里压根没有环··。
什么叫做环呢,如果你已知a,b的和,b c的和,c a的和,那么好了,三元一次不等式方程组,可解,也就是所有数字都不可知,只知道和,但是只要构成环就能求出来,这是我爆搜没做的地方。
爆搜版本:
#include
#include
#include
#include
using namespace std;
const int N=3e5+10;
int a[N];
vector<int>g[N];
int cnt=0;
void dfs(int x){
a[x]=1;
cnt++;
for(int i=0;i<(int)g[x].size();i++){
int to=g[x][i];
if(a[to]==0){
dfs(to);
}
}
g[x].clear();
}
int main()
{
int n,m; scanf("%d%d",&n,&m);
while(m--){
int opt,x,y; scanf("%d",&opt);
if(opt==1){
scanf("%d",&x);
if(a[x]==0)
dfs(x);
}
else{
scanf("%d%d",&x,&y);
if(a[x]^a[y]){
if(a[x]==0)
dfs(x);
else
dfs(y);
}
else if(a[x]==1&&a[y]==1){
;
}
else{
g[x].push_back(y);
g[y].push_back(x);
}
}
printf("%d\n",cnt);
}
return 0;
}
赛后队友给的并查集版本(找环就是看看这两个点是不是原本就在一个连通块即可,但是重边还是不能做,重边还得单独搞)
#include
#include
#include
using namespace std;
const int maxn=3e5+5;
int n,m;
bool flag[maxn];
int num[maxn];
int fa[maxn];
int opt;
int x,y;
int cnt;
int find(int a)
{
while(a!=fa[a])
a=fa[a];
return a;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i)
{
num[i]=1;
fa[i]=i;
}
for(int i=1;i<=m;++i)
{
scanf("%d",&opt);
if(cnt==n)
printf("%d\n",n);
else
if(opt==1)
{
scanf("%d",&x);
int tmp=find(x);
if(!flag[tmp])
{
flag[tmp]=1;
cnt+=num[tmp];
}
}
else
{
scanf("%d %d",&x,&y);
if(x==y)
{
int tmp=find(x);
if(!flag[tmp])
{
flag[tmp]=1;
cnt+=num[tmp];
}
}
else
{
int f1=find(x);
int f2=find(y);
if(flag[f1]&&!flag[f2])
{
flag[f2]=1;
fa[f1]=f2;
cnt+=num[f2];
num[f2]+=num[f1];
}
else
if(!flag[f1]&&flag[f2])
{
flag[f1]=1;
fa[f1]=f2;
cnt+=num[f1];
num[f2]+=num[f1];
}
else
if(!flag[f1]&&!flag[f2])
{
if(f1!=f2)
{
fa[f1]=f2;
num[f2]+=num[f1];
}
}
}
}
printf("%d\n",cnt);
}
return 0;
}