水题
CF1887E
考虑对于建点,一个点 ( i , j ) (i,j) (i,j) 有数字 w w w 即为第 i i i 行的点 i i i 和第 j j j 列的点连了一条边权为 w w w 的边。
考虑到图是二分图,且 2 n 2n 2n 点, 2 n 2n 2n 边,必有偶环。
考虑找到一个偶环。
讨论环长是否为四的倍数。若不为四的倍数,则询问任意一对正对面的点对应的格子;若为四的倍数,错开一位即可。
考虑之后一定有一部分偶环,还是环上边权两两不同,最终缩到一个 4 4 4 条边的环即可。
void dfs(int u,int pa){
if(ok)
return ;
vis[u]=1;
s[++sid]=u;
for(int v:E[u]){
if(v==pa||vis[v]==2)
continue;
if(vis[v]==1&&!ok){
if(v<=n)
e.push_back(v);
while(s[sid]!=v){
e.push_back(s[sid]);
// cout<<s[sid]<<" "<<sid<<"\n";
s[sid--]=0;
}
if(v>n)
e.push_back(v);
ok=1;
return ;
}
dfs(v,u);
}
s[sid--]=0;
vis[u]=2;
}
void solve(){
cin>>n;
m=n<<1;
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
col[i][j]=0;
for(int i=1;i<=m;i++)
E[i].clear();
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
y+=n;
E[x].push_back(y);
E[y].push_back(x);
col[x][y]=col[y][x]=i;
}
fill(vis+1,vis+m+1,0);
fill(s+1,s+m+1,0);
e.clear();
for(int i=1;i<=m;i++){
if(!vis[i]){
ok=sid=0;
dfs(i,0);
if(ok)
break;
}
}
// cout<<e.size()<<"\n";
while(e.size()>4){
int sz=e.size();
int l=0,r=(sz%4==0?sz/2+1:sz/2);
cout<<"? "<<e[l]<<" "<<e[r]-n<<"\n";
cout.flush();
int c;
cin>>c;
col[e[l]][e[r]]=col[e[r]][e[l]]=c;
int res=0;
for(int i=l;i<r;i++)
if(col[e[i]][e[i+1]]==c)
res=1;
vector<int>e1;
e1.clear();
e1.push_back(e[l]);
e1.push_back(e[r]);
if(res==1)
for(int i=r+1;i<sz;i++)
e1.push_back(e[i]);
else
for(int i=r-1;i>=1;i--)
e1.push_back(e[i]);
swap(e,e1);
}
cout<<"! "<<e[0]<<" "<<e[2]<<" "<<e[1]-n<<" "<<e[3]-n<<"\n";
cout.flush();
string ok;
cin>>ok;
if(ok=="OK")
return ;
while(1);
}
CF453B
考虑到,所有 b i < 59 b_i<59 bi<59,小于 59 59 59 共 16 16 16 个质数。
考虑 f [ i , S ] f[i,S] f[i,S] 表示前 i i i 数,后面不可使用的质数集合为 S S S。
考虑转移,记 p [ i ] p[i] p[i] 为 i i i 的质数集合,则 f [ i ] [ S ] = min f [ i − 1 ] [ p [ k ] ] + ∣ k − a [ i ] ∣ f[i][S]=\min f[i-1][p[k]]+|k-a[i]| f[i][S]=minf[i−1][p[k]]+∣k−a[i]∣,转移条件为现在 S S S 包含 p [ k ] p[k] p[k]。
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
for(int j=0;j<1<<M;j++){
f[i][j]=1e9;
for(int k=1;k<59;k++){
if((j&prs[k])==prs[k]){
int nx=f[i-1][j^prs[k]]+abs(x-k);
if(nx<f[i][j])
f[i][j]=nx,p[i][j]=k;
}
}
}
}
int no=(1<<M)-1;
for(int i=n;i>=1;i--){
ans[i]=p[i][no];
no^=prs[p[i][no]];
}
for(int i=1;i<=n;i++)
cout<<ans[i]<<" ";
}
CF1783D
DP
考虑 f [ i , j ] f[i,j] f[i,j] 表示考虑到 i i i 操作,此时 a [ i + 1 ] a[i+1] a[i+1] 为 j j j 的方案数。
转移时考虑去重当前位为 0 0 0 的情况。
void solve(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
int v=300*n;
f[1][a[2]+v]=1;
for(int i=2;i<n;i++){
for(int j=-v;j<=v;j++){
f[i][a[i+1]+j+v]+=f[i-1][j+v];
f[i][a[i+1]-j+v]+=f[i-1][j+v];
if(!j) f[i][a[i+1]-j+v]-=f[i-1][j+v];
f[i][a[i+1]+j+v]%=mod;
f[i][a[i+1]-j+v]%=mod;
}
}
int ans=0;
for(int i=0;i<=2*v;i++)
ans+=f[n-1][i],ans%=mod;
cout<<ans<<"\n";
}
CF279D
DP
f [ i ] [ S ] f[i][S] f[i][S] 表示拼出了 a i a_i ai,当前保存的数集合为 S S S 时最小的 m m m。
考虑滚动数组。
考虑优化,发现转移为
意味着只用转移一次即可。
那么就做完了,时间复杂度 O ( n 2 n ) O(n2^n) O(n2n)
for(int i=0;i<n;i++)
cin>>a[i];
memset(f,0x3f3f3f3f,sizeof f);
f[0][1]=1;
for(int i=1;i<n;i++){
int np=i&1,pp=np^1;
memset(f[np],0x3f3f3f3f,sizeof f[np]);
memset(vis,0,sizeof vis);
vector<pair<int,int> >D;
D.clear();
for(int j=0;j<i;j++)
for(int k=j;k<i;k++){
if(a[j]+a[k]==a[i])
D.push_back({j,k});
}
if(D.empty()){
cout<<-1<<"\n";
return 0;
}
for(auto [x,y]:D){
for(int S=0;S<1<<i;S++){
if(vis[S])
continue;
if((S>>x)&1)if((S>>y)&1){
vis[S]=1;
f[np][S|(1<<i)]=min(f[np][S|(1<<i)],f[pp][S]+1);
for(int j=0;j<i;j++)
if((S>>j)&1)
f[np][(S^(1<<j))|(1<<i)]=min(f[np][(S^(1<<j))|(1<<i)],f[pp][S]);
}
}
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<1<<n;i++)
ans=min(ans,f[(n-1)&1][i]);
CF1368E
考虑如此删除,发现删除点数不超过 4 7 n \frac{4}{7} n 74n
我们以此给节点标记度数,删掉了就清零。
那么一旦某个点历史最大度数达到 3 3 3 就要删除了。
queue<int>Q;
for(int i=1;i<=n;i++)
if(!deg[i])
d[i]=1,Q.push(i);
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int v:E[u]){
deg[v]--;
if(!deg[v])
Q.push(v);
if(vis[v])
continue;
d[v]=max(d[u]+1,d[v]);
if(d[v]>=3)
ans.push_back(v),d[v]=0,cnt++,vis[v]=1;
}
}
luogu6583
考虑计数 i j d j \frac{ij}{dj} djij,其中 d = 2 a 5 b , j d=2^a5^b,j d=2a5b,j 不包含 2 , 5 2,5 2,5
记 f ( n ) f(n) f(n) 为 n n n 以内只含 2 , 5 2,5 2,5 的数个数,这个可以在 O ( log 2 n log 5 n ) O(\log_2n\log_5n) O(log2nlog5n) 时间内解决。
枚举 j j j,贡献为 n j × f ( n j ) \frac{n}{j}\times f(\frac{n}{j}) jn×f(jn)
发现这个形式可以整除分块。
但是需要统计一段区间内不含 2 , 5 2,5 2,5 的数的个数,这个可以容斥简单做。
inline int ga(int x){
return x-x/2-x/5+x/10;
}
inline int calc(int x){
int res=0,o=lg2(x);
for(int i=0;i<=o;i++){
res+=lg5(x>>i)+1;
}
return res;
}
void solve(){
cin>>n;
int ans=0;
for(int l=1,r;l<=n;l=r+1){
r=(n/(n/l));
int base=ga(r)-ga(l-1);
int v=n/l;
ans+=base*v*calc(v);
}
cout<<ans<<"\n";
}