必须吐槽一下……这场比赛真是原题大战……我一开始只看出来几道题是SCU上的……后来有人我告诉我是2015四川赛区的一整套题……出题人是厉害……
A.Easy Math
比赛的时候做一个大胆地猜想……只有每个数开方后都是整数它们的和才都是整数。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16
#define pa pair<int,int>
using namespace std;
int main(){
int n,x;
while (scanf("%d",&n)!=EOF){
for (int i=1;i<=n;i++)
scanf("%d",&x);
bool flag=1;
if (sqrt(x)-floor(sqrt(x))>0.0) {
flag=0;
}
if (flag) puts("Yes");
else puts("No");
}
return 0;
}
B.Carries
注意到0≤ai≤10^9,这样只需要枚举在第10^k有多少个数产生进位就可以了。
注意到答案和顺序无关,只需要保证一对数不能计重就行,也就是说排序不影响结果。
那么枚举k,令s=10^k 。
每次对数组都mod s ,然后排序。
题目转化为:现在已知a< s, b < s,现在已知a,求有多少个b使得a+b>=s。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16
using namespace std;
const int MAXN=1e+5+1000;
int n;
LL A[MAXN],B[MAXN];
int main(){
while (~scanf("%d",&n)){
for (int i=0;i<n;i++) scanf("%lld",&A[i]);
sort(A,A+n);
LL cnt=0,s=10;
for (int k=1;k<=10;k++,s*=10){
for (int i=0;i<n;i++) B[i]=A[i]%s;
sort(B,B+n);
for (int i=0;i<n;i++){
LL t=s-B[i];
if (B[n-1]>=t){
int pos=lower_bound(B,B+n,t)-B;
if (pos>i) cnt+=n-pos;
else cnt+=n-(i+1);
}
}
}
printf("%lld\n",cnt);
}
return 0;
}
C.Censor
套用KMP模板求得失配数组,再开一个栈保存没有被删除的字符以及已经匹配了a字符串的字符个数。如果在b中完整匹配到了a,则把length(a)个字符出栈,然后取栈顶保存的已匹配的字符个数继续去匹配a,栈为空则从0开始。复杂度O(n)。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16
using namespace std;
const int N = 5e6+5;
struct PP {
char c;
int cnt;
}sta[N];
int f[N];
char w[N], p[N], res[N];
int wlen, plen;
int hehe(){
int cur=0,cnt=0;
for (int i=0;i<plen;i++){
while (cnt && w[cnt]!=p[i]) cnt=f[cnt];
if (w[cnt]==p[i]){
sta[cur++]=(PP){p[i],cnt+1};
cnt++;
}
else {
sta[cur++]=(PP){p[i],0};
cnt=0;
}
if (cnt==wlen){
cur-=wlen;
if (cur==0) cnt=0;
else cnt=sta[cur-1].cnt;
}
}
return cur;
}
void getFail(char *p, int *f, int m) {
f[0]=0, f[1]=0;
for(int i = 1;i < m; i++) {
int j = f[i];
while(j && p[i] != p[j]) j = f[j];
f[i+1] = (p[i] == p[j])?j+1:0;
}
}
void solve(){
wlen=strlen(w),plen=strlen(p);
getfail(w,f,wlen);
int num=hehe();
for (int i=0;i<num;i++)
putchar(sta[i].c);
puts("");
}
int main(){
while(scanf("%s%s",w,p)!=EOF)
solve();
return 0;
}
D.Vertex Cover
只搜索前30个点的选与不选,如果还有边没有被覆盖则一定选非前30的点来覆盖。这样一定可以搜索到最优解。
如果一个点不选则起相邻的点必选。
所以我们初始化所有的点都选,然后搜索哪些点不选。
当我们搜索到一个点的时候,判断它相邻的点是否有不选的,若有则必选;否则我们可以选择不选该点。这样搜索出来的状态对于前30个点之间的边来说一定是合法的。再加上最优性剪枝就可过这题。
搜索的做法:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16
using namespace std;
struct node{
int to,next;
}E[550*550*2];
pair<int,int>a[35];
bool use[35],tu[35][35];
int N,ans,du[550],flag,fuck[550];
int head[550],num;
int n,m;
void prepare(){
memset(head,-1,sizeof(head));
num=0;
}
void add(int u,int v){
E[num].to=v;
E[num].next=head[u];
head[u]=num++;
E[num].to=u;
E[num].next=head[v];
head[v]=num++;
}
void dfs(int id,int val){
if (val+flag>ans) return ;
if (id==N+1){
ans=min(ans,val+flag);
return ;
}
bool ok=1;
int w;
for (int i=1;i<=N;i++){
if (tu[id][i] && use[i]) ok=0;
}
if (ok){
use[id]=1;
for (int i=head[id];i+1;i=E[i].next){
w=E[i].to;
if (fuck[w]==0) flag++;
fuck[w]++;
}
dfs(id+1,val);
for (int i=head[id];i+1;i=E[i].next){
w=E[i].to;
fuck[w]--;
if (fuck[w]==0) flag--;
}
use[id]=0;
}
dfs(id+1,val+1);
}
int main(){
int i,u,v;
while (scanf("%d%d",&n,&m)!=EOF){
prepare();
memset(tu,false,sizeof(tu));
memset(fuck,0,sizeof(fuck));
memset(du,0,sizeof(du));
for (int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
if (u>30 || v>30) add(u,v);
du[u]++, du[v]++;
if (u<=30 || v<=30)
tu[u][v]=tu[v][u]=1;
}
flag=0;
N=min(30,n);
memset(use,false,sizeof(use));
ans=m;
dfs(1,0);
printf("%d\n",ans);
}
return 0;
}
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16
using namespace std;
int n,m;
int link[505],mp[505][505];
bool used[505];
bool dfs(int u){
int v;
for (v=1;v<=n;v++)
if (mp[u][v] && !used[v]){
used[v]=1;
if (link[v]==-1 || dfs(link[v])){
link[v]=u;
return true;
}
}
return false;
}
int hungary(){
int res=0;
int u;
memset(link,-1,sizeof(link));
for (u=1;u<=n;u++){
memset(used,0,sizeof(used));
if (dfs(u)) {res++;}
}
return res;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
memset(mp,0,sizeof(mp));
for (int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
mp[u][v]=1;
mp[v][u]=1;
}
printf("%d\n",hungary()/2);
}
}
E.Rectangle
一道思考题,推推结论,找找规律就好了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16
using namespace std;
LL n,m,k;
int main()
{
while(~scanf("%lld%lld%lld",&n,&m,&k)){
LL ans=0;
for(LL x=1;x<=n;x++){
LL tmax=(k-2*x)/2;
if(tmax>0){
if(tmax>=m)tmax=m;
if(tmax%2==0){
ans+=(n-x+1)*(m+m-tmax+1)*(tmax/2);
}
else {
ans+=(n-x+1)*((m+m-tmax+1)/2)*tmax;
}
}
}
printf("%lld\n",ans);
}
return 0;
}
枚举值为10000的数。确定值为10000的数的位置后,环就断成了一条链。我们可以用动态规划求出(1,i)的数的不增序列的最大和,同理求出(i,n)的不增序列的最大和。然后枚举断点即可。在动态规划的过程中需要用到树状数组优化转移(线段树会T)。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16
const int nn = 110000;
int n;
int a[nn];
int c[nn*2];
int f1[nn],f2[nn];
int tree[11000];
inline int lowbit(int x)
{
return x&(-x);
}
int getmax(int id)
{
int re=0;
while(id>0)
{
re=max(re,tree[id]);
id-=lowbit(id);
}
return re;
}
void add(int id,int val)
{
if(id==0)
return ;
for(int i=id;i<=10000;i+=lowbit(i))
{
tree[i]=max(tree[i],val);
}
}
int solve(int id)
{
int i;
f1[0]=0;
for(i=1;i<=10000;i++)
tree[i]=0;
int tem;
for(i=id+1;i<id+n;i++)
{
if(c[i]==10000)
{
f1[i-id]=f1[i-id-1];
continue;
}
tem=getmax(10000-c[i]+1)+c[i];
f1[i-id]=max(f1[i-id-1],tem);
add(10000-c[i]+1,tem);
}
f2[n]=0;
for(i=1;i<=10000;i++)
tree[i]=0;
for(i=id+n-1;i>=id+1;i--)
{
if(c[i]==10000)
{
f2[i-id]=f2[i-id+1];
continue;
}
tem=getmax(10000-c[i]+1)+c[i];
f2[i-id]=max(f2[i-id+1],tem);
add(10000-c[i]+1,tem);
}
int re=0;
for(i=0;i<=n-1;i++)
re=max(re,f1[i]+f2[i+1]);
return re;
}
int ve[20],lv;
int main()
{
int i;
while(scanf("%d",&n)!=EOF)
{
lv=0;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
c[i]=a[i];
c[i+n]=a[i];
if(a[i]==10000)
ve[lv++]=i;
}
int ans=0;
for(i=0;i<lv;i++)
{
ans=max(ans,solve(ve[i]));
}
printf("%d\n",ans+10000);
}
return 0;
}
G.Party
神建图的一道题
完备匹配的最小字典序。 先找出最大匹配,然后枚举从小到每个点枚举最优状态,删边然后继续寻找增广路。
我抄的这道题代码……因为带字典序我就不会做了……毕竟太弱了(结果居然拿了个FB)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int mmax = 60;
int max_num[mmax],min_num[mmax];
int L[mmax],R[mmax];
bool G[mmax][mmax];
int n,m1,m2;
void init()
{
memset(G,0,sizeof G);
for(int i=1;i<=n;i++)
{
L[i]=min_num[i]=1;
R[i]=max_num[i]=n;
}
}
int link[mmax];
bool vis[mmax];
int Match[mmax];
bool match(int x)
{
for(int i=1;i<=n;i++)
{
if(G[x][i] && !vis[i])
{
vis[i]=1;
if(link[i]==-1 || match(link[i]))
{
link[i]=x;
Match[x]=i;
return 1;
}
}
}
return 0;
}
int hungury()
{
int cnt=0;
memset(link,-1,sizeof link);
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof vis);
if(match(i))
cnt++;
}
return cnt;
}
int main()
{
int a,b,c;
while(~scanf("%d %d %d",&n,&m1,&m2))
{
init();
for(int i=1;i<=m1;i++)
{
scanf("%d %d %d",&a,&b,&c);
for(int j=a;j<=b;j++)
min_num[j]=max(min_num[j],c);
L[c]=max(L[c],a);
R[c]=min(R[c],b);
}
for(int i=1;i<=m2;i++)
{
scanf("%d %d %d",&a,&b,&c);
for(int j=a;j<=b;j++)
max_num[j]=min(max_num[j],c);
L[c]=max(L[c],a);
R[c]=min(R[c],b);
}
for(int i=1;i<=n;i++)
{
for(int j=min_num[i];j<=max_num[i];j++)
{
if(L[j]<=i && i<=R[j])
G[i][j]=1;
}
}
int num = hungury();
if(num==n)
{
// for(int i=1;i<=n;i++)
// printf("%d%c",Match[i],i==n?'\n':' ');
for(int i=1;i<=n;i++)
{
int tmp=Match[i];
G[i][tmp]=0;
link[tmp]=-1;
bool fg=0;
memset(vis,0,sizeof vis);
for(int j=1;j<tmp;j++)
{
if(!vis[j] && G[i][j])
{
vis[j]=1;
if(link[j]==-1 || match(link[j]))
{
link[j]=i;
Match[i]=j;
fg=1;
break;
}
}
}
if(!fg)
{
G[i][tmp]=1;
link[tmp]=i;
}
tmp=Match[i];
for(int j=1;j<=n;j++)
G[j][tmp]=0;
}
for(int i=1;i<=n;i++)
printf("%d%c",Match[i],i==n?'\n':' ');
}
else
puts("-1");
}
return 0;
}
I.Travel
不会,不想补了,POI2007原题。
J.Right turn
模拟题。
对于出现过的每一个x建立一个set 集合,对于y也如此。然后查找要走到哪个点即可,主要要对状态记录,判断是否无限循环。
#include <set>
#include <map>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long int LL;
const int M = 1009,INF = 0x3fffffff;
vector<pair<int, vector<int> > > vx, vy;
struct place {int x, y ,dir;}s;
vector<struct place> repeat;
bool operator == (struct place a, struct place b) {
return a.x == b.x && a.y == b.y && a.dir == b.dir;
}
void input(int n) {
for (int i = 0; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
bool key = true;
for (int j = 0; j < vx.size(); j++) {
if (vx[j].first == x) {
vx[j].second.push_back(y);
key = false;
break;
}
}
if (key) {
pair<int, vector<int> >p;
p.first = x;
p.second.push_back(y);
vx.push_back(p);
}
key = true;
for (int j = 0; j < vy.size(); j++) {
if (vy[j].first == y) {
vy[j].second.push_back(x);
key = false;
break;
}
}
if (key) {
pair<int, vector<int> >p;
p.first = y;
p.second.push_back(x);
vy.push_back(p);
}
}
}
bool move(void) {
if (s.dir == 0) {
for (int i = 0; i < vy.size(); i++) {
if (vy[i].first == s.y) {
int temp = INF;
bool ok = false;
for (int j = 0; j < vy[i].second.size(); j++) {
if(vy[i].second.at(j) > s.x && vy[i].second.at(j) < temp) {
temp = vy[i].second.at(j);
ok = true;
}
}
s.x = temp - 1;
s.dir = 1;
return ok;
}
}
}
if (s.dir == 1) {
for (int i = 0; i < vx.size(); i++) {
if (vx[i].first == s.x) {
int temp = -INF;
bool ok = false;
for (int j = 0; j < vx[i].second.size(); j++) {
if(vx[i].second.at(j) < s.y && vx[i].second.at(j) > temp) {
temp = vx[i].second.at(j);
ok = true;
}
}
s.y = temp + 1;
s.dir = 2;
return ok;
}
}
}
if (s.dir == 2) {
for (int i = 0; i < vy.size(); i++) {
if (vy[i].first == s.y) {
int temp = -INF;
bool ok = false;
for (int j = 0; j < vy[i].second.size(); j++) {
if(vy[i].second.at(j) < s.x && vy[i].second.at(j) > temp) {
temp = vy[i].second.at(j);
ok = true;
}
}
s.x = temp + 1;
s.dir = 3;
return ok;
}
}
}
if (s.dir == 3) {
for (int i = 0; i < vx.size(); i++) {
if (vx[i].first == s.x) {
int temp = INF;
bool ok = false;
for (int j = 0; j < vx[i].second.size(); j++) {
if(vx[i].second.at(j) > s.y && vx[i].second.at(j) < temp) {
temp = vx[i].second.at(j);
ok = true;
}
}
s.y = temp - 1;
s.dir = 0;
return ok;
}
}
}
return false;
}
bool judge_infinite(void) {
for (int i = 0; i < repeat.size(); i++) {
if (s == repeat[i]) return true;
}
return false;
}
int main(void) {
//problem: soj4445, address:http://acm.scu.edu.cn/soj/problem.action?id=4445
int n;
while (~scanf("%d", &n)) {
vx.clear();
vy.clear();
repeat.clear();
input(n);
s.x = s.y = s.dir = 0;
for (int i = 0;; i++) {
repeat.push_back(s);
if (!move()) {
printf("%d\n", i);
break;
}
if (judge_infinite()) {
printf("-1\n");
break;
}
}
}
return 0;
}