CF题解
D. Training Session 1700
https://codeforces.com/contest/1598/problem/D
题解:由于正面条件少不好做,我们考虑反面,其能给我们提供相当多的性质。即:3题中,至少有两题难度相同,至少有两题主题一致。而没有两题一摸一样,故假定有3题x,y,z,则x和y难度相同,x和z难度相同。现在问题变得非常好做了,我们只需预处理难度一致的题,和主题一致的题数量,接着枚举i,每次ans+同主题题数*同难度题数,最后取反即可。
#include<bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int N=2e5+100;
int a[N],b[N],p[N],f[N][4],res=0;
pair<int,int> c[N];
int v[N],vv[N];
void solve(){
int n,ans=0,t=0;cin>>n;
res=0;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
v[a[i]]++;
vv[b[i]]++;
}
ans=n*(n-1)*(n-2)/6;
for(int i=1;i<=n;i++){
ans-=(v[a[i]]-1)*(vv[b[i]]-1);
}
cout<<ans<<endl;
for(int i=1;i<=n;i++) v[i]=vv[i]=0;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
}
E. Staircases 2100 dp
https://codeforces.com/contest/1598/problem/E
题解:我们可以处理出没经过任何操作时候的所有阶梯数,令f(i,j,0/1)表示到(i,j)的且此步为下或右数量,则对x,y求和即可得阶梯数,f(i,j,0)=f(i,j-1,1)+1,f(i,j,1)同理。
我们考虑如何处理操作:我们发现每步操作其实只会影O(n)个格子的数量,故我们可以每步更新f(i,j,0/1),受到影响的格子是有阶梯经过(x,y)的所有格子。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1005;
int ans=0;
int n,m,Q;
int f[N][N][3],g[N][N][3],v[N][N],vis[N][N],s[N][N];
queue<pair<int,int> >q;
signed main(){
cin>>n>>m>>Q;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
f[i][j][1]=f[i-1][j][2]+1;
f[i][j][2]=f[i][j-1][1]+1;
if(i==1) f[i][j][1]=0;
if(j==1) f[i][j][2]=0;
ans+=f[i][j][1]+f[i][j][2]+1;
s[i][j]=f[i][j][1]+f[i][j][2]+1;
}
}
for(int i=1;i<=Q;i++){
int x,y;cin>>x>>y;
if(!v[x][y]){
v[x][y]=1;
int w=f[x][y][1]+f[x][y][2]+1;
ans-=w;
vis[x][y]=i;f[x][y][1]=-1,f[x][y][2]=-1;
q.push({x+1,y});q.push({x,y+1});
for(int j=x+1,k=y+1;j<=n&&k<=m;j++,k++){
q.push({j,k});q.push({j+1,k});q.push({j,k+1});
}
while(q.size()){
auto [x,y]=q.front();q.pop();
if(x>n||y>m) continue;
if(f[x][y][1]==-1) continue;
int w=f[x][y][1]+f[x][y][2]+1;
f[x][y][1]=f[x-1][y][2]+1;
f[x][y][2]=f[x][y-1][1]+1;
if(x==1) f[x][y][1]=0;
if(y==1) f[x][y][2]=0;
w=w-f[x][y][1]-f[x][y][2]-1;
ans-=w;
}
}
else{
v[x][y]=0;
f[x][y][1]=f[x-1][y][2]+1;
f[x][y][2]=f[x][y-1][1]+1;
if(x==1) f[x][y][1]=0;
if(y==1) f[x][y][2]=0;
ans+=f[x][y][1]+f[x][y][2]+1;
q.push({x+1,y});q.push({x,y+1});
for(int j=x+1,k=y+1;j<=n&&k<=m;j++,k++){
q.push({j,k});q.push({j+1,k});q.push({j,k+1});
}
while(q.size()){
auto [x,y]=q.front();q.pop();
if(x>n||y>m) continue;
int w=f[x][y][1]+f[x][y][2]+1;
if(f[x][y][1]==-1) continue;
f[x][y][1]=f[x-1][y][2]+1;
f[x][y][2]=f[x][y-1][1]+1;
if(x==1) f[x][y][1]=0;
if(y==1) f[x][y][2]=0;
w=w-f[x][y][1]-f[x][y][2]-1;
ans-=w;
}
}
cout<<ans<<endl;
}
}
D. Treelabeling 2100 构造 gq
https://codeforces.com/contest/1605/problem/D
题解:不好入手,我们西先考虑怎么样能够赢得最多,显然是每个点都是孤立点,那么每个点都可走。这样是可行的吗?我们又发现由于是树,我们可以分层的odd/even考虑,由抽屉原理,其中必有一个是<=n/2,而两点不可互通的条件是二进制最高位不同,而最高位相同的数的个数为2的幂次,故我们总可以用几类(二进制最高位数为k的数有f(k)个)f(k)组成较小那个层数,即保证层与层不互通。故得到最优构造。
#include<bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int N=2e5+10;
int m;
int a[N],d[N],c[2],vis[N];
queue<int> q,qq;
vector<int> g[N];
void dfs(int x,int fa){
d[x]=d[fa]^1;
c[d[x]]++;
for(auto it:g[x]){
if(it==fa) continue;
dfs(it,x);
}
}
void dfss(int x,int fa){
if(d[x]==m){
a[x]=q.front();q.pop();
}
else{
a[x]=qq.front();qq.pop();
}
for(auto it:g[x]){
if(it==fa) continue;
dfss(it,x);
}
}
void solve(){
while(q.size()) q.pop();
while(qq.size()) qq.pop();
c[0]=c[1]=0;
int n;cin>>n;
for(int i=1;i<=n;i++) g[i].clear(),vis[i]=0;
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
g[u].pb(v);g[v].pb(u);
}
if(n==1){
cout<<1<<endl;return;
}
dfs(1,0);
if(c[0]<c[1]) m=0;
else m=1;
int w=c[m],cnt=0;
while(w){
if(w%2){
for(int i=(1<<cnt);i<(1<<(cnt+1));i++)
q.push(i),vis[i]=1;
}
w/=2;
cnt++;
}
for(int i=1;i<=n;i++){
if(!vis[i]) qq.push(i);
}
dfss(1,0);
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
cout<<endl;
}
signed main(){
int T;
cin>>T;
while(T--) solve();
}