赛后感言:虽然迟到了一个小时,但是一个小时过了两题,而且第三题思路也很清楚,前两题都是一次过的。
b.H and V - SMUOJ
思路:数据量很少,计算各行各列各有多少黑块,二进制枚举删除那些列哪些列,在用总的减去删去的,如果sun-sun1==k,ans++,最后输出答案就好(这是代码一的内容)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=100010;
#define PII pair<int,int>
char p[10][10];
int a[10];
int b[10];
void solve(){
int h,w,k;
cin>>h>>w>>k;
int sum=0;
int ans=0;
for(int i=1;i<=h;i++){
int nn=0;
for(int j=1;j<=w;j++){
cin>>p[i][j];
if(p[i][j]=='#') nn++,sum++;
}
a[i]=nn;
}
for(int i=1;i<=w;i++){
int nn=0;
for(int j=1;j<=h;j++){
if(p[j][i]=='#') nn++;
}
b[i]=nn;
}
for(int i=0;i<(1<<h);i++){
for(int j=0;j<(1<<w);j++){
int o1=0;
int aa[10];
int bb[10];
for(int l=0;l<h;l++){
if(((i>>l)&1)==1) aa[o1++]=l+1;
}
int o2=0;
for(int l=0;l<w;l++){
if(((j>>l)&1)==1) bb[o2++]=l+1;
}
// cout<<o1<<o2<<endl;
int sum1=0;
for(int g=0;g<o1;g++){
for(int v=0;v<o2;v++){
if(p[aa[g]][bb[v]]=='#') sum1--;
}
}
for(int g=0;g<o1;g++) sum1+=a[aa[g]];
for(int g=0;g<o2;g++) sum1+=b[bb[g]];
if(sum-sum1==k) ans++;
//化简的二进制求法
//
//int n,m,k;
//cin>>n>>m>>k;
//int ans=0;
//bool ar[n][m];
//for(int i=0;i<n;i++)for(int j=0;j<m;j++){
//char x;cin>>x;
//ar[i][j]= x == '#';
//}
// for(int i=0;i<(1<<n);i++){
// for(int j=0;j<(1<<m);j++){
// int cnt=0;
// for(int x=0;x<n;x++){
// for(int y=0;y<m;y++){
// if(ar[x][y]&&(i&(1<<x))&&(j&(1<<y)))cnt++;
// }
// }
// if(cnt==k)ans++;
// }
// }
}
}
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
// cin>>t;
while(t -- ){
solve();
}
return 0;
}
c.Moving Piece - SMUOJ
题解:通过案例推倒,我们发现这是一定存在循环的,于是我们对每一个点求它出现过的循环节,将每一次周期当中出现过的数存入v[i]当中,用sum存储一个周期的价值,如果该周期sum<0,则最大值一定出现在第一次循环当中,如果sum>0,则最大值一定出现在最后一个无论是否完整的周期当中,最后做一些细节处理即可
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int,int>
int n,k;
int p[5010],c[5010],mp[5010];
//mp记录当前循环经过了哪些点
int sum[5010];
vector<int>v[5010];
//第i次循环当中出现的数
void solve(){
cin>>n>>k;
int ans=-(1e9+10);
for(int i=1;i<=n;i++) cin>>p[i];
for(int i=1;i<=n;i++) cin>>c[i];
for(int i=1;i<=n;i++){
memset(mp,0,sizeof mp);
int now=i;
while(1){//查找周期
now=p[now];
if(mp[now]) break;
mp[now]=1;
sum[i]+=c[now];
v[i].push_back(c[now]);
}
int l=v[i].size();
int d=k/l;
int m=k%l;
now=0;//一标二用,记录循环中出现过的答案
if(sum[i]>0){//可以多次循环累计,且最大值一定出现在最后一次完整或不完整的循环
if(m==0){
now=(d-1)*sum[i];
m=l;
}
else now=d*sum[i];
for(int j=0;j<m;j++) now+=v[i][j],ans=max(ans,now);
}
else {//最大值一定出现在第一次循环
if(d==0){
for(int j=0;j<m;j++) now+=v[i][j],ans=max(ans,now);
}
else{
for(int j=0;j<l;j++) now+=v[i][j],ans=max(ans,now);
}
}
}
cout<<ans<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}
d.Sum of Divisors - SMUOJ
思路:由于查找每一个数的因数个数的过场是重复的,所以我们需要先预处理一下,代码类似晒法求素数的过程,这样只需要遍历一遍就可以找到所有数的因数个数,最后输出就好。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[10000010];
void solve(){
int n;
cin>>n;
memset(a,0,sizeof a);
//参考筛法求素数的方法给每一个数查找因数个数
for(int i=1;i<n;i++){
int j;
if(i==1 )j=i+1;
else j=i;
for(;i*j<=n;j++){
if(i==j) a[i*j]++;
else a[i*j]+=2;
}
}
int sum=0;
a[1]=1;
for(int i=1;i<=n;i++) sum+=i*a[i];
cout<<sum<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
// cin>>t;
while(t -- ){
solve();
}
return 0;
}
f.Rem of Sum is Num
思路:
区间和 用 前缀和处理
因此题目就变成求 有多少对 ( S [ i ] − S [ j ] ) % k = i − j
即 :
S [ i ] − S [ j ] = k ∗ a + i − j(a 一个常数)
S [ i ] − i − ( s [ j ] − j ) = k ∗ a
( s [ i ] − i ) % k = ( s [ j ] − j ) % k
因此我们可以先预处理除一个 长度为m i n ( k − 1 , n ) 、的区间
然后我们直接计算即可
题解:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define endl '\n'
const int N = 2e5+10;
int x,f[N];
map<int,int> mp;
void solve()
{
int n,k;cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>f[i];
f[i] += f[i-1];
}
for(int i=1;i<=n;i++)
f[i] = (f[i] - i)%k;
int pos = min(n,k-1);
ll sum = 0;
for(int i= 0 ;i<=pos;i++)
{
sum+=mp[f[i]];
mp[f[i]] ++;
}
for(int i=pos+1;i<=n;i++)
{
mp[f[i-k]] --;
sum+=mp[f[i]];
mp[f[i]]++;
}
cout<<sum<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
solve();
return 0;
}