头大
这个暑假完就要去搞NOIP了。。。
暑假55天也就20次测试。。。为防万一我还是每次测试玩都写个总结。。
出题人继续神奇的把NOI的题强行改成了NOIP的题系列。
欧拉大法好。orz。(但这次没啥卵用)
Day2 (130/300)
T1 操作(100/100)
问题描述
Mstdream 有 n 个数:a1,a2,a3,a4….an
每一次,他选出最小的数 x 和最大的数 y,并且将它们都修改为 y-x,直到所有数都一
样。如果所有数据已经都一样了,则不用修改了。
Mstdream 想知道,最后剩下的数是什么?
如果无法让所有数一样,输出“No!!!!!!!!!!”(不包括引号)
输入格式
第一行 N
接下来一行 N 个数 ai
输出格式
输出最后的数
如果无法让所有数一样,输出“No!!!!!!!!!!”(不包括引号)
Sample Input
3
1 2 3
Sample Output
2
Sample Input
2
5 5
Sample Output
5
数据规模
30%数据保证有解
60%数据保证 n<=5
100%数据保证 n<=10 1<=ai<=50000
智障题不解释。暴力枚举。
STD.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
using namespace std;
int n,a[15];
int cnt=10000000;
int main()
{
freopen("change.in","r",stdin);
freopen("change.out","w",stdout);
cin >> n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);
while(cnt--)
{
if(a[1]==a[n])break;
int x = a[n]-a[1];
a[1] = x;a[n] = x;
sort(a+1,a+n+1);
}
if(a[1]!=a[n])cout << "No!!!!!!!!!!" << endl;
else cout << a[1] << endl;
}
T2 跳蚤 (30/100)
Z 城市居住着很多只跳蚤。在 Z 城市周六生活频道有一个娱乐节目。一只跳蚤将被请上
一个高空钢丝的正中央。钢丝很长,可以看作是无限长。节目主持人会给该跳蚤发一张卡片。
卡片上写有 N+1 个自然数。其中最后一个是 M,而前 N 个数都不超过 M,卡片上允许有相同
的数字。跳蚤每次可以从卡片上任意选择一个自然数 S,然后向左,或向右跳 S 个单位长度。
而他最终的任务是跳到距离他左边一个单位长度的地方,并捡起位于那里的礼物。
比如当 N=2,M=18 时,持有卡片(10, 15, 18)的跳蚤,就可以完成任务:他可以先向左
跳 10 个单位长度,然后再连向左跳 3 次,每次 15 个单位长度,最后再向右连跳 3 次,每次
18 个单位长度。而持有卡片(12, 15, 18)的跳蚤,则怎么也不可能跳到距他左边一个单位
长度的地方。
当确定 N 和 M 后,显然一共有 M^N 张不同的卡片。现在的问题是,在这所有的卡片中,
有多少张可以完成任务。
Input
两个整数 N 和 M
Output
可以完成任务的卡片数。
Sample Input
2 4
Sample Output
12
Hint
这 12 张卡片分别是:
(1, 1, 4), (1, 2, 4), (1, 3, 4), (1, 4, 4), (2, 1, 4), (2, 3, 4),
(3, 1, 4), (3, 2, 4), (3, 3, 4), (3, 4, 4), (4, 1, 4), (4, 3, 4)
范围
对于 30%数据:N<=5,M<=5;
对于 100%数据 N<=15,M<=100000000
自做聪明地搞了个欧拉函数套高精度结果做到一半出题人跑进来说对2^64取模。。妈蛋30多位的数高精度根本搞不定。。虽然算出了准确答案还是GG了。。。
MY.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cctype>
#include<iomanip>
#include<map>
#include<set>
#include<conio.h>
#include<vector>
#include<queue>
using namespace std;
struct Wint:vector<int>//用标准库vector做基类,完美解决位数问题,同时更易于实现
{
//将低精度转高精度的初始化,可以自动被编译器调用
//因此无需单独写高精度数和低精度数的运算函数,十分方便
Wint(int n=0)//默认初始化为0,但0的保存形式为空
{
push_back(n);
check();
}
Wint& check()//在各类运算中经常用到的进位小函数,不妨内置
{
while(!empty()&&!back())pop_back();//去除最高位可能存在的0
if(empty())return *this;
for(int i=1; i<size(); ++i)
{
(*this)[i]+=(*this)[i-1]/10;
(*this)[i-1]%=10;
}
while(back()>=10)
{
push_back(back()/10);
(*this)[size()-2]%=10;
}
return *this;//为使用方便,将进位后的自身返回引用
}
};
//输入输出
istream& operator>>(istream &is,Wint &n)
{
string s;
is>>s;
n.clear();
for(int i=s.size()-1; i>=0; --i)n.push_back(s[i]-'0');
return is;
}
ostream& operator<<(ostream &os,const Wint &n)
{
if(n.empty())os<<0;
for(int i=n.size()-1; i>=0; --i)os<<n[i];
return os;
}
//比较,只需要写两个,其他的直接代入即可
//常量引用当参数,避免拷贝更高效
bool operator!=(const Wint &a,const Wint &b)
{
if(a.size()!=b.size())return 1;
for(int i=a.size()-1; i>=0; --i)
if(a[i]!=b[i])return 1;
return 0;
}
bool operator==(const Wint &a,const Wint &b)
{
return !(a!=b);
}
bool operator<(const Wint &a,const Wint &b)
{
if(a.size()!=b.size())return a.size()<b.size();
for(int i=a.size()-1; i>=0; --i)
if(a[i]!=b[i])return a[i]<b[i];
return 0;
}
bool operator>(const Wint &a,const Wint &b)
{
return b<a;
}
bool operator<=(const Wint &a,const Wint &b)
{
return !(a>b);
}
bool operator>=(const Wint &a,const Wint &b)
{
return !(a<b);
}
//加法,先实现+=,这样更简洁高效
Wint& operator+=(Wint &a,const Wint &b)
{
if(a.size()<b.size())a.resize(b.size());
for(int i=0; i!=b.size(); ++i)a[i]+=b[i];
return a.check();
}
Wint operator+(Wint a,const Wint &b)
{
return a+=b;
}
//减法,返回差的绝对值,由于后面有交换,故参数不用引用
Wint& operator-=(Wint &a,Wint b)
{
if(a<b)swap(a,b);
for(int i=0; i!=b.size(); a[i]-=b[i],++i)
if(a[i]<b[i])//需要借位
{
int j=i+1;
while(!a[j])++j;
while(j>i)
{
--a[j];
a[--j]+=10;
}
}
return a.check();
}
Wint operator-(Wint a,const Wint &b)
{
return a-=b;
}
//乘法不能先实现*=,原因自己想
Wint operator*(const Wint &a,const Wint &b)
{
Wint n;
n.assign(a.size()+b.size()-1,0);
for(int i=0; i!=a.size(); ++i)
for(int j=0; j!=b.size(); ++j)
n[i+j]+=a[i]*b[j];
return n.check();
}
Wint& operator*=(Wint &a,const Wint &b)
{
return a=a*b;
}
//除法和取模先实现一个带余除法函数
Wint divmod(Wint &a,const Wint &b)
{
Wint ans;
for(int t=a.size()-b.size(); a>=b; --t)
{
Wint d;
d.assign(t+1,0);
d.back()=1;
Wint c=b*d;
while(a>=c)
{
a-=c;
ans+=d;
}
}
return ans;
}
Wint operator/(Wint a,const Wint &b)
{
return divmod(a,b);
}
Wint& operator/=(Wint &a,const Wint &b)
{
return a=a/b;
}
Wint& operator%=(Wint &a,const Wint &b)
{
divmod(a,b);
return a;
}
Wint operator%(Wint a,const Wint &b)
{
return a%=b;
}
//顺手实现一个快速幂,可以看到和普通快速幂几乎无异
Wint pow(const Wint &n,const Wint &k)
{
if(k.empty())return 1;
if(k==2)return n*n;
if(k.back()%2)return n*pow(n,k-1);
return pow(pow(n,k/2),2);
}
/以上皆为某大佬的代码/
int getphi(int n)
{
int m=sqrt(n+0.5);
int ans=n;
for(int i=2;i<=m;i++)
if(n%i==0)
{
ans = ans/i*(i-1);
while(n%i==0)
n/=i;
}
if(n>1)ans=ans/n*(n-1);
return ans;
}
int main()
{
//freopen("flea.in","r",stdin);
//freopen("flea.out","w",stdout);
int n,m;
//Wint c=18446744073709551616;
cin >> n >> m;
Wint x=n,y=m,z=m-getphi(m);
cout << (pow(y,x)-pow(z,x)) << endl;
}
STD.CPP
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#define LL unsigned long long
using namespace std;
LL n,m,t,x,ans=1;
LL s[55];
int main()
{
//freopen("flea.in","r",stdin);
//freopen("flea.out","w",stdout);
cin>>n>>m;
x=m;
LL q=(LL)(sqrt((double)m))+1;
for(LL i=2;i<=q;i++)
{
while(m%i==0)
{
s[++t]=i;
m/=i;
}
}
if(m>1)
s[++t]=m;
sort(s+1,s+t+1);
t=unique(s+1,s+t+1)-s-1;
for(LL i=1;i<=n;i++)
ans*=x;
for(LL i=1;i<(1<<t);i++)
{
LL f=i,p=1;
LL now=1;
for(LL j=1;j<=t;j++)
{
if(f&1)
now*=s[j],p=-p;
f>>=1;
}
LL tmp=1;
for(LL i=1;i<=n;i++)
tmp*=(x/now);
ans+=p*tmp;
}
cout<<ans<<endl;
return 0;
}
T3 攻打饺子国 (0/100)
题目描述
Mstdream 国王正在率军攻打饺子国!
饺子国属于岛国,Mstdream 的军队只能空投进入饺子国;饺子国由 n 个城市,m 条双向道
路构成(可能有重边自环);对于一个城市 i,Mstdream 在该城市空投一个士兵的的代价是
bi,这个城市需要 ai 个士兵才能攻占。对于一条道路 i(设为 u 到 v),需要 u 点和 v 点一
共有 wi 个士兵才能攻占。我们假设 Mstdream 的士兵在攻占过程中不会损伤,士兵攻占完
一个地方后可以去攻占其他地方
对于一条攻占了的道路,Mstdream 的士兵可以随意走动。
Mstdream 的计划是攻占所有饺子国的城市(请注意,道路可以不被攻占),为了打败饺子
国,Mstdream 请你求出最小的代价。
Input
第一行 2 个数 n 和 m
接下来 n 行,每行两个数 ai,bi
接下来 m 行,每行三个数 ui,vi,wi
Output
一行一个数,代表最小的代价
Sample Input
3 2
10 5
20 10
10 3
1 2 22
2 3 200
Sample Output
140
样例说明:
在 3 点空投 10 人,代价 30
在 1 点空投 22 人,攻占完 1 后攻占 1 到 2 的道路,并且攻占 2,代价 110
总代价 140
范围
30% n<=5,m<=10
60% n<=2000,m<=4000
100% n<=100000,m<=300000
0 ≤ ai , bi ≤ 1000000
0 ≤ ci ≤ 1000000
数据较大,请勿使用 cin
用最小生成树来做的想法和题解一样,方法几乎一样,但细节处没做到位。我认了orz
MY.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
using namespace std;
int n,m,u,v,w,ans=0;
const int kkk=100050;
int first[kkk],fa[kkk],a[kkk],b[kkk];
struct node{
int u,v,val,next;
}side[7*kkk];
struct done{
int num,val;
}hehe[7*kkk];
int cnt=0;
void addedge(int u,int v,int val)
{
side[++cnt].u = u;
side[cnt].v = v;
side[cnt].val = val;
side[cnt].next = first[u];
first[u] = cnt;
hehe[cnt].val = val;
hehe[cnt].num = cnt;
}
bool comp(const done &a,const done &b){ return a.val < b.val;}
int getfa(int x)
{
if(fa[x]!=x)return getfa(fa[x]);
return x;
}
bool visit[kkk];
void kruskal()
{
sort(hehe+1,hehe+cnt,comp);
for(int i=1;i<=cnt;i++)
{
int u = side[hehe[i].num].u;
int v = side[hehe[i].num].v;
int u1 = getfa(u);
int v1 = getfa(v);
if(u1!=v1)
{
visit[hehe[i].num]=true;
fa[u1] = v1;
}
}
}
int ans1,ans2;
bool exist[kkk];
void dfs(int root)
{
exist[root] = true;
ans1 = min(ans1,b[root]);
ans2 = max(ans2,a[root]);
for(int i=first[root];i;i=side[i].next)
if(visit[i]&&!exist[side[i].v])
{
ans2 = max(ans2,side[i].val);
dfs(side[i].v);
}
}
int main()
{
freopen("dumpling.in","r",stdin);
freopen("dumpling.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&b[i]);
addedge(0,i,a[i]*b[i]); fa[0]=0; fa[i]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
fa[u]=u; addedge(u,v,w);
fa[v]=v; addedge(v,u,w);
}
kruskal();
for(int i=first[0];i;i=side[i].next)
if(visit[i])
{
ans1 = 9999999; ans2 = 0;
dfs(side[i].v);
ans += ans1*ans2;
}
cout << ans << endl;
}
STD.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
using namespace std;
const int kkk=100050;
int n,m,u,ii,w,fa[kkk];
long long a[kkk],b[kkk],v[kkk],ans=0;
struct node{
int u,v;
long long val;
}side[7*kkk];
int cnt=0;
void addedge(int u,int v,int val)
{
side[++cnt].u = u;
side[cnt].v = v;
side[cnt].val = val;
}
bool comp(const node &a,const node &b){ return a.val < b.val;}
int getfa(int x)
{
if(fa[x]==x) return x;
fa[x] = getfa(fa[x]);
return fa[x];
}
bool visit[kkk];
void kruskal()//题解在细节上做的更到位
{
sort(side+1,side+m+1,comp);
for(int i=1;i<=m;i++)
{
int u1 = getfa(side[i].u);
int v1 = getfa(side[i].v);
if(u1!=v1)
{
fa[v1] = u1;
a[u1] = max(max(a[u1],a[v1]),side[i].val);
b[u1] = min(b[u1],b[v1]);
v[u1] = min(v[u1]+v[v1],(long long)a[u1]*b[u1]);
}
}
}
int main()
{
//freopen("dumpling.in","r",stdin);
//freopen("dumpling.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&b[i]);
v[i]=(long long)a[i]*b[i];
fa[i]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&ii,&w);
fa[u]=u; fa[ii]=ii;
addedge(u,ii,w);
}
kruskal();
for(int i=1;i<=n;i++)if(fa[i]==i)ans+=v[i];
cout << ans << endl;
}
感想:
还是要学会推公式,推的要准,不然全挂。现在想法和题解几乎差不多(那是因为题没之前那么变态),但要注意细节,思考要全面。这个暑假集训下来乱想空想的现象已经很少了。加油。