1602C. Array Elimination(逻辑运算&)
题意:
给出一组数,对于一个数k,每次可以选择k个位置进行若干次操作:k个位置上的数减去这k个数的与值。
求出所有能够使得这组数都变为 0 的数 k。
思路:
要把所有数都变为0,也就是将所有数的数位变为0。
每次操作,减去的是k个数的与值。所以,为了将一个数位变为0,减去的这个与值的该数位也要为0。
为了让与值的该数位为0,那么挑选的k个位置上的数该数位都为1。
因为可以操作若干次,所以对于一列数位的x个1来说,可以选择x的因子y个1,能够将这y个数的该数位都化为0,然后进行若干次。
综合考虑所有数位,那么满足条件的 k 就为30个数位中,1的个数的所有公约数。
一个数的一个数位为1,减去一个该数位同样为1的数,这个数位就变成0了。 数位之间不相互影响。
求一组数的所有约数 == 求gcd的所有因子。
对于这样位运算的题,不要看整个数,要看每个数位之间的操作和变化。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N], ans[N];
int f[35];
void pd(int x)
{
for(int i=0;i<=30;i++)
{
if(x&(1<<i)) f[i]++;
}
}
void prim(int x)
{
int cnt=0;
for(int i=1;i<=x/i;i++)
{
if(x%i==0){
ans[++cnt]=i;
if(x/i!=i) ans[++cnt]=x/i;
}
}
sort(ans+1,ans+cnt+1);
for(int i=1;i<=cnt;i++) cout<<ans[i]<<" ";
cout<<endl;
}
int main(){
Ios;
cin>>T;
while(T--)
{
cin>>n;
mem(f,0);
for(int i=1;i<=n;i++){
int x;cin>>x;
pd(x);
}
int g;
for(int i=0;i<=30;i++){
if(i==0) g=f[i];
else g=__gcd(g,f[i]);
}
if(!g) for(int i=1;i<=n;i++) cout<<i<<" ";
prim(g);
}
return 0;
}
546.C. Soldier and Cards(模拟)
题意:
两个人进行纸牌游戏。每人有一堆纸牌,每次从堆顶取出一个比较。
较大者,先将对方纸牌放到堆底,再将自己纸牌放到堆底。无牌者败。
问,如果最终可以分出胜负,则输出局数和胜者;否则输出-1。
思路:
队列模拟。
如何判断最终是否有解呢?
可以用string来记录状态,然后判断当前两者的状态是否之前出现过。
也可以判断循环次数。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
int que1[N], que2[N];
//手写队列,标记状态
int main()
{
Ios;
int s;cin>>s;
int h1=0,t1=-1,h2=0,t2=-1;
cin>>n;
for(int i=1;i<=n;i++){
int x;cin>>x;
que1[++t1]=x;
}
cin>>m;
for(int i=1;i<=m;i++){
int x;cin>>x;
que2[++t2]=x;
}
int flag=0,cnt=0;
while(1)
{
cnt++;
int x=que1[h1];h1++;
int y=que2[h2];h2++;
if(x>y) que1[++t1]=y,que1[++t1]=x;
else que2[++t2]=x,que2[++t2]=y;
string s1,s2;
for(int i=h1;i<=t1;i++){
s1+=(char)(que1[i]+'0');
}
for(int i=h2;i<=t2;i++){
s2+=(char)(que2[i]+'0');
}
if(mp[{
s1,s2}]) break;
else mp[{
s1,s2}]=1;
if(t1<h1||t2<h2){
if(t1<h1) flag=2;
else flag=1;
break;
}
}
if(flag){
cout<<cnt<<" "<<flag;
}
else cout<<-1;
}
//stl,判断循环次数
int main(){
Ios;
int s;cin>>s;
cin>>n;
queue<int> que1,que2;
for(int i=1;i<=n;i++){
int x;cin>>x;
que1.push(x);
}
cin>>m;
for(int i=1;i<=m;i++){
int x;cin>>x;
que2.push(x);
}
int flag=0,cnt=0;
while(1)
{
cnt++;
int x=que1.front();que1.pop();
int y=que2.front();que2.pop();
if(x>y) que1.push(y),que1.push(x);
else que2.push(x),que2.push(y);
if(!que1.size()||!que2.size()){
if(!que1.size()) flag=2;
else flag=1;
break;
}
else if(cnt>=10000000) break;
}
if(flag){
cout<<cnt<<" "<<flag;
}
else cout<<-1;
return 0;
}
1520.E. Arranging The Sheep(绝对值求和)
题意:
给出n个位置,每个位置上为空或者有羊。
每次可以将羊往左移或者往右移,移动的位置需要为空。
问,最少移动多少次,所有羊聚在一起(位置相邻)?
思路:
和之前做过的 士兵站队 相同思路。是那题的简化。
设定最终起始羊位置为 x,那么后面羊位置依次为 x + 1 , x + 2 , x + 3... x+1,x+2,x+3... x+1,x+2,x+3...
那么,位置为 ai 的羊 到其最终位置所需要的移动次数为: ∣ x + i − 1 − a i ∣ |x+i-1 - ai| ∣x+i−1−ai∣;
我们知道绝对值相加最小值为中位数:
若使得 ∣ x − a i ∣ + ∣ x − a i + 1 ∣ + ∣ x − a i + 2 ∣ + . . . + ∣ x − a i + n ∣ |x-a_i| + |x-a_{i+1}| + |x-a_{i+2}| +...+ |x-a_{i+n}| ∣x−ai∣+∣x−ai+1∣+∣x−ai+2∣+...+∣x−ai+n∣ 最小,x 应当取 ai 的中位数。
这里同样是求绝对值相加的最小值,那么我们可以化为上式: ∣ x − ( a i − i + 1 ) ∣ |x - (ai-i+1)| ∣x−(ai−i+1)∣。
所以,起始位置 x 的取值为数组 {ai-i+1} 的中位数。
对于数组{a},其中位数为排过序的 a[n/2+1]
。
Code:
const int N = 2000010, mod = 1e9+7;
int T, n, m, a[N];
int main(){
Ios;
cin>>T;
while(T--)
{
cin>>n;
int cnt=0;
for(int i=1;i<=n;i++){
char c;cin>>c;
if(c=='*') a[++cnt]=i;
}
for(int i=1;i<=cnt;i++){
a[i]-=i-1;
}
ll st=a[cnt/2+1],ans=0;
for(int i=1;i<=cnt;i++){
ans+=abs(a[i]-st);
}
cout<<ans<<endl;
}
return 0;
}
277.A. Learning Languages(并查集)
题意:
一共n个人,m种语言。每人掌握0~m种语言。每人每学习一种语言,花费1。
问,最少花费多少,能使所有人沟通无碍。(每个人都可以和其余n-1人沟通)
思路:
把所有相同的语言的人合并起来。这样,所有人就分成了若干个集合。
对于每个人,遍历其余所有人:
如果两人不在同一集合中,那么就花费1学习对方语言,这样两个集合合并。
最终要特判一下,如果所有人掌握的语言个数都为0,输出n。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
vector<int> v[N];
int pre[N];
int find(int x){
int t=x;
while(pre[x]!=x) x=pre[x];
pre[t]=x;
return x;
}
int main(){
Ios;
cin>>n>>m;
int flag=0;
for(int i=1;i<=n;i++)
{
pre[i]=i;
int k;cin>>k;
if(k) flag=1;
for(int j=1;j<=k;j++){
int x;cin>>x;
v[x].push_back(i);
}
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<v[i].size();j++)
{
pre[find(v[i][j])]=pre[find(v[i][j-1])];
}
}
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
{
if(find(i)!=find(j))
cnt++,pre[find(i)]=find(j);
}
}
if(!flag) cout<<n;
else cout<<cnt;
return 0;
}