D - The Number of Imposters
题目大意
有 \(n\) 个人,他们只说真话或只说假话。给出 \(m\) 条信息,格式为 "第 \(i\) 个人说第 \(j\) 个人只说真话/假话"。请你判断最多的谎话者人数。如果矛盾输出 \(-1\)。
解法
一个挺妙的转化是:当第 \(i\) 个人说第 \(j\) 个人只说真话时,他们一定是一种类型;同理,第 \(i\) 个人说第 \(j\) 个人只说假话时,他们的类型不同。
用带权并查集或建虚点都可以做。
代码
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' or s<'0')
f|=(s=='-');
while(s>='0' and s<='9')
x=(x<<1)+(x<<3)+(s^48),
s=getchar();
return f?-x:x;
}
template <class T>
inline void write(const T x) {
if(x<0) {
putchar('-'),write(-x);
return;
}
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <iostream>
using namespace std;
const int maxn = 2e5+5;
int f[maxn],val[maxn];
int tot[maxn][2],n,m;
char s[20];
int Find(int x) {
if(x==f[x]) return x;
int fa=Find(f[x]);
val[x]^=val[f[x]];
return f[x]=fa;
}
int main() {
for(int T=read(9);T;--T) {
n=read(9),m=read(9);
for(int i=1;i<=n;++i)
f[i]=i,val[i]=0,
tot[i][0]=tot[i][1]=0;
bool flag=false;
for(int i=1;i<=m;++i) {
int x=read(9),y=read(9);
int u=Find(x),v=Find(y);
scanf("%s",s+1);
bool op=(s[1]=='i');
if(u^v) {
f[u]=v;
val[u]=val[x]^val[y]^op;
}
else if(val[x]^val[y]^op)
flag=true;
}
if(flag) {
puts("-1");
continue;
}
for(int i=1;i<=n;++i)
++tot[Find(i)][val[i]];
int ans=0;
for(int i=1;i<=n;++i)
if(Find(i)==i)
ans+=max(tot[i][0],tot[i][1]);
print(ans,'\n');
}
return 0;
}
E2 - Rubik's Cube Coloring (hard version)
解法
容易发现,当一个点固定颜色后,它只会影响从它开始往上一条链的点。其它的点的颜色种数乘上 \(4\) 的幂次即可。由于树高只有 \(60\),所以最多有 \(60n=120000\) 个点被影响。令 \(dp_{i,j}\) 为第 \(i\) 个点颜色为 \(j\) 的染色方案数,只考虑被影响点即可。
优化 \(\mathtt{dp}\) 状态数是一种好方法。
代码
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' or s<'0')
f|=(s=='-');
while(s>='0' and s<='9')
x=(x<<1)+(x<<3)+(s^48),
s=getchar();
return f?-x:x;
}
template <class T>
inline void write(const T x) {
if(x<0) {
putchar('-'),write(-x);
return;
}
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <map>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
int k,n,pin,dp[2005*60][6],co[2005];
ll a[2005];
string s;
map <ll,int> id;
map <string,int> mp;
vector <int> adj[8];
int qkpow(int x,ll y) {
y=(y%(mod-1)+mod-1)%(mod-1);
int r=1;
while(y) {
if(y&1) r=1ll*r*x%mod;
x=1ll*x*x%mod; y>>=1;
}
return r;
}
inline int inc(int x,int y) {
return x+y>=mod?x+y-mod:x+y;
}
int dfs(ll o,int c) {
int ind=id[o];
if(~dp[ind][c])
return dp[ind][c];
if(ind<=n and (c^co[ind]))
return 0;
int ans1=0,ans2=0;
for(auto v:adj[c]) {
if(id.count(o<<1))
ans1 = inc(ans1,dfs(o<<1,v));
if(id.count(o<<1|1))
ans2 = inc(ans2,dfs(o<<1|1,v));
}
if(!id.count(o<<1)) ans1=1;
if(!id.count(o<<1|1)) ans2=1;
return dp[ind][c]=1ll*ans1*ans2%mod;
}
int main() {
mp["white"]=0;
mp["yellow"]=1;
mp["green"]=2;
mp["blue"]=3;
mp["red"]=4;
mp["orange"]=5;
for(int i=0;i<6;++i)
for(int j=0;j<6;++j)
if((i>>1)^(j>>1))
adj[i].push_back(j);
memset(dp,-1,sizeof dp);
k=read(9),pin=n=read(9);
for(int i=1;i<=n;++i) {
a[i]=read(9ll);
cin>>s; co[i]=mp[s];
id[a[i]]=i;
}
for(int i=1;i<=n;++i)
for(ll j=a[i]>>1;j;j>>=1)
if(!id.count(j))
id[j]=++pin;
int ans=0;
for(int i=0;i<6;++i)
ans = inc(ans,dfs(1,i));
print(1ll*ans*qkpow(4,(1ll<<k)-pin-1)%mod,'\n');
return 0;
}