NOIP 模拟 1003
天空龙
奥西里斯有a 个红色,b 个黄色,c 个蓝色,他想用画出最好的画,可是需要至少x 个红色,y 个黄色和z 个蓝色,似乎并不够。别担心,奥西里斯会魔法!他可以把任何两个同种颜色转化为一个另一种颜色!请问他能不能完成呢?
t<=100,0<=a,b,c,x,y,z<=1000000。
题解
稍微想一下就知道不存在拿对自己有用的去帮助别人,最后让别人帮自己,那么直接看多出来的能填多少空就行。
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int t,a,b,c,x,y,z; int main(){ freopen("osiris.in","r",stdin); freopen("osiris.out","w",stdout); scanf("%d",&t); while(t--){ int tot=0,need=0; scanf("%d%d%d%d%d%d",&a,&b,&c,&x,&y,&z); if(a>x) tot+=(a-x)>>1; else need+=x-a; if(b>y) tot+=(b-y)>>1; else need+=y-b; if(c>z) tot+=(c-z)>>1; else need+=z-c; printf("%s\n",tot>=need ? "YES" : "NO"); } } /* 3 4 4 0 2 1 2 5 6 1 2 7 2 3 3 3 2 2 2 */
巨神兵
欧贝利斯克的巨神兵很喜欢有向图,有一天他找到了一张n 个点m 条边的有向图。
欧贝利斯克认为一个没有环的有向图是优美的,请问这张图有多少个子图(即选定一个边集)是优美的?答案对1,000,000,007 取模。
对于40%的数据n<=5,m<=20;
对于60%的数据n<=10;
对于80%的数据n<=15;
对于100%的数据n<=17。
题解
40%的做法,就暴力枚举边集最后用拓扑判环。
#include<ctime> #include<queue> #include<vector> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int mod=1000000007; const int maxn=20; const int maxm=250; int n,m,ret; int cnt,du[maxn],d[maxn]; vector<int> c[maxn]; struct edge{ int x,y; }e[maxm]; template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x = f ? -x : x ; } int topsort(){ queue<int> q; int tot=0; for(int i=1;i<=n;i++){ d[i]=du[i]; if(!du[i]) q.push(i),tot++; } while(!q.empty()){ int x=q.front(); q.pop(); for(unsigned int i=0;i<c[x].size();i++){ int y=c[x][i]; if(d[y]){ d[y]--; if(!d[y]) q.push(y),tot++; } } } return tot==n; } void dfs(int s){ if(!topsort()) return ; if(s>m){ ret++; if(ret>=mod) ret-=mod; return ; } dfs(s+1); du[e[s].y]++; c[e[s].x].push_back(e[s].y); dfs(s+1); c[e[s].x].pop_back(); du[e[s].y]--; } int main(){ freopen("obelisk.in","r",stdin); freopen("obelisk.out","w",stdout); read(n);read(m); for(int i=1;i<=m;i++) read(e[i].x),read(e[i].y); dfs(1); printf("%d",ret); }
对于60%的数据,考虑把有向图分层(貌似是一种套路),分层就是类似topsort一样的,第一层是最初入度为0的点,去掉他们入度为0的是第二层....
然后考虑f[i][j],i为现在选取的点集,j为最后一层的点集,考虑一个可以转移的点集k:满足与i无交集,k中每个点都与j有连边(和i其他点无所谓)
考虑转移答案f[i|k]+=f[i][j]*num1,num1为选边的方案数,是每个k中的点选边方案数的乘积,k中点选边方案数是至少选一条连向j的边*选取连向i中其他点的边的方案数。
#include<ctime> #include<queue> #include<vector> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int mod=1000000007; const int maxn=100; const int maxm=250; int n,m,tot; int cnt,head[maxn]; ll ret,pow2[maxn],f[1030][1030]; struct edge{ int x,y,next; }e[maxm]; template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x = f ? -x : x ; } void add(int x,int y){ e[++cnt]=(edge){x,y,head[x]}; head[x]=cnt; } bool get(int x,int s){return (1<<(x-1))&s;} int main(){ freopen("obelisk.in","r",stdin); freopen("obelisk.out","w",stdout); read(n);read(m); pow2[0]=1; for(int i=1;i<=m;i++) pow2[i]=pow2[i-1]*2; for(int i=1;i<=m;i++){ int x,y; read(x);read(y); add(y,x); } tot=(1<<n)-1; for(int i=1;i<=tot;i++) f[i][i]=1; for(int i=1;i<=tot;i++)//总共选的点 for(int j=i;j;j=(j-1)&i){//最后一层,是i的子集 if(!f[i][j]) continue; int cx=tot^i;//i的补集,k的选取集合 for(int k=cx;k;k=(k-1)&cx){ ll num1=1; for(int o=1;o<=n;o++){ if(!get(o,k)) continue;//找出k里面的节点 ll cnt1=0,cnt2=0;//连向j的边,连向i中除了j的边 for(int l=head[o];l;l=e[l].next){ int y=e[l].y; if(get(y,j)) cnt1++; else if(get(y,i)) cnt2++; } num1=num1*(pow2[cnt1]-1)%mod;//必须选一个 num1=num1*pow2[cnt2]%mod; if(!num1) break;//只能是与j无连边 } if(!num1) continue; f[i|k][k]=(f[i|k][k]+f[i][j]*num1%mod)%mod; } } for(int i=1;i<=tot;i++) ret=(ret+f[tot][i])%mod; printf("%d",ret); }
对于100%的数据,考虑不要第二维,会统计重复所以加个容斥(我也不知道为啥容斥是这样)
具体看代码吧,理解到上面的就差不多可以理解了(除了容斥)
#include<ctime> #include<queue> #include<vector> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int mod=1000000007; const int maxn=20; const int maxm=250; int n,m,tot; int mp[maxn][maxn]; ll pow2[maxm],f[1<<17]; ll a[1<<17],b[1<<17];//边数 int num[1<<17];//容斥系数,按二进制下1的个数 template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x = f ? -x : x ; } bool get(int x,int s){return (1<<(x-1))&s;} int main(){ freopen("obelisk.in","r",stdin); freopen("obelisk.out","w",stdout); read(n);read(m); pow2[0]=1; for(int i=1;i<=m;i++) pow2[i]=pow2[i-1]*2%mod; for(int i=1;i<=m;i++){ int x,y; read(x);read(y); mp[x-1][y-1]=1; } tot=(1<<n)-1; num[0]=-1; for(int i=1;i<=tot;i++) num[i]=num[i>>1]*( i&1 ? -1 : 1 ); f[0]=1; for(int i=0;i<tot;i++){ for(int j=0;j<n;j++) b[1<<j]=0; for(int j=0;j<n;j++) if((1<<j)&i) for(int k=0;k<n;k++) b[1<<k]+=mp[j][k]; a[0]=0; int j=tot^i; for(int k=(j-1)&j;;k=(k-1)&j){ int now=j^k,o=now&-now;//为了从小到大枚举 a[now]=a[now^o]+b[o]; f[i|now]=(f[i|now]+f[i]*pow2[a[now]]*num[now])%mod; if(!k) break; } } printf("%d",(f[tot]+mod)%mod); }
太阳神
太阳神拉很喜欢最小公倍数,有一天他想到了一个关于最小公倍数的题目。
求满足如下条件的数对(a,b)对数:a,b 均为正整数且a,b<=n 而lcm(a,b)>n。其中的lcm 当然表示最小公倍数。答案对1,000,000,007取模
对于100%的数据n<=10000000000。
题解
不会数论,只有照着别人说
有杜教筛啥的,不过我这就讲我理解得到的
补集转换(顶不住)
那么就考虑lcm(i,j)<=n的
$\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(i,j)\leq n]$
$\sum_{k}\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(\frac{i}{k},\frac{j}{k})=1][\frac{ij}{k}\leq n]$
$\sum_{k}\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{d|gcd(\frac{i}{k},\frac{j}{k})}\mu (d)[\frac{ij}{k}\leq n]$
$\sum_{k}\mu (d)\sum_{d|\frac{i}{k}}^{n}\sum_{d|\frac{j}{k}}^{n}[\frac{ij}{k}\leq n]$
$\sum_{k}\mu (d)[\frac{ij}{k}\leq \frac{n}{d^2}]$
$\mu (d)\sum_{k}[\frac{i}{k}\frac{j}{k}k\leq \frac{n}{d^2}]$
$\frac{n}{d^2}\geq 1,\therefore d\leq \sqrt n$
所以考虑枚举d,找出符合的$\frac{i}{k},\frac{j}{k},k$
$假设\frac{i}{k}\leq \frac{j}{k}\leq k$
$\frac{i}{k}\leq \sqrt[3]{\frac{n}{d^2}},\frac{j}{k}*\frac{j}{k}\leq\frac{n}{d^2i}$
所以就枚举前两个可以得到k的范围,就知道满足条件的有多少个了,然后考虑三者可以互换(因为本身没有大小关系),分类讨论一下即可。
最后答案是n*n-求出的答案
#include<cmath> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int mod=1000000007; const int maxn=100000; ll n,m,ans; int prime[maxn+5],mu[maxn+5]; bool not_prime[maxn+5]; void init(){ mu[1]=1; for(int i=2;i<=maxn;i++){ if(!not_prime[i]){ prime[++prime[0]]=i; mu[i]=-1; } for(int j=1;j<=prime[0]&&i*prime[j]<=maxn;j++){ not_prime[i*prime[j]]=true; if(i%prime[j]) mu[i*prime[j]]=-mu[i]; else { mu[i*prime[j]]=0; break; } } } } int main(){ freopen("ra.in","r",stdin); freopen("ra.out","w",stdout); scanf("%lld",&n); init(); m=sqrt(n+0.5); for(int d=1;d<=m;d++){ ll val=n/d/d,ret=0; for(ll i=1;i*i*i<=val;i++) for(ll j=i;j*j<=val/i;j++){ ll c=val/i/j-j+1; //i*j*k<=val,i<=j<=k //c:k的取值个数 if(i==j) ret=(ret+1+(c-1)*3)%mod;//1:j==k,否则i,j,k有三种排列 else ret=(ret+3+(c-1)*6)%mod; } ans=(ans+mu[d]*ret)%mod; } ans=(n%mod)*(n%mod)%mod-ans; ans=(ans%mod+mod)%mod; printf("%lld",ans); }